github.com/lzy4123/fabric@v2.1.1+incompatible/common/ledger/blockledger/fileledger/impl_test.go (about) 1 /* 2 Copyright IBM Corp. 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 cb "github.com/hyperledger/fabric-protos-go/common" 17 ab "github.com/hyperledger/fabric-protos-go/orderer" 18 "github.com/hyperledger/fabric-protos-go/peer" 19 "github.com/hyperledger/fabric/common/flogging" 20 cl "github.com/hyperledger/fabric/common/ledger" 21 "github.com/hyperledger/fabric/common/ledger/blockledger" 22 "github.com/hyperledger/fabric/common/metrics/disabled" 23 "github.com/hyperledger/fabric/protoutil" 24 "github.com/stretchr/testify/assert" 25 "github.com/stretchr/testify/mock" 26 ) 27 28 var genesisBlock = protoutil.NewBlock(0, nil) 29 30 func init() { 31 flogging.ActivateSpec("common.ledger.blockledger.file=DEBUG") 32 } 33 34 type testEnv struct { 35 t *testing.T 36 location string 37 flf blockledger.Factory 38 } 39 40 func initialize(t *testing.T) (*testEnv, *FileLedger) { 41 name, err := ioutil.TempDir("", "hyperledger_fabric") 42 assert.NoError(t, err, "Error creating temp dir: %s", err) 43 44 p, err := New(name, &disabled.Provider{}) 45 assert.NoError(t, err) 46 flf := p.(*fileLedgerFactory) 47 fl, err := flf.GetOrCreate("testchannelid") 48 assert.NoError(t, err, "Error GetOrCreate channel") 49 fl.Append(genesisBlock) 50 return &testEnv{location: name, t: t, flf: flf}, fl.(*FileLedger) 51 } 52 53 func (tev *testEnv) tearDown() { 54 tev.shutDown() 55 err := os.RemoveAll(tev.location) 56 if err != nil { 57 tev.t.Fatalf("Error tearing down env: %s", err) 58 } 59 } 60 61 func (tev *testEnv) shutDown() { 62 tev.flf.Close() 63 } 64 65 type mockBlockStore struct { 66 blockchainInfo *cb.BlockchainInfo 67 resultsIterator cl.ResultsIterator 68 block *cb.Block 69 envelope *cb.Envelope 70 txValidationCode peer.TxValidationCode 71 defaultError error 72 getBlockchainInfoError error 73 retrieveBlockByNumberError error 74 } 75 76 func (mbs *mockBlockStore) AddBlock(block *cb.Block) error { 77 return mbs.defaultError 78 } 79 80 func (mbs *mockBlockStore) GetBlockchainInfo() (*cb.BlockchainInfo, error) { 81 return mbs.blockchainInfo, mbs.getBlockchainInfoError 82 } 83 84 func (mbs *mockBlockStore) RetrieveBlocks(startNum uint64) (cl.ResultsIterator, error) { 85 return mbs.resultsIterator, mbs.defaultError 86 } 87 88 func (mbs *mockBlockStore) RetrieveBlockByHash(blockHash []byte) (*cb.Block, error) { 89 return mbs.block, mbs.defaultError 90 } 91 92 func (mbs *mockBlockStore) RetrieveBlockByNumber(blockNum uint64) (*cb.Block, error) { 93 return mbs.block, mbs.retrieveBlockByNumberError 94 } 95 96 func (mbs *mockBlockStore) RetrieveTxByID(txID string) (*cb.Envelope, error) { 97 return mbs.envelope, mbs.defaultError 98 } 99 100 func (mbs *mockBlockStore) RetrieveTxByBlockNumTranNum(blockNum uint64, tranNum uint64) (*cb.Envelope, error) { 101 return mbs.envelope, mbs.defaultError 102 } 103 104 func (mbs *mockBlockStore) RetrieveBlockByTxID(txID string) (*cb.Block, error) { 105 return mbs.block, mbs.defaultError 106 } 107 108 func (mbs *mockBlockStore) RetrieveTxValidationCodeByTxID(txID string) (peer.TxValidationCode, error) { 109 return mbs.txValidationCode, mbs.defaultError 110 } 111 112 func (*mockBlockStore) Shutdown() { 113 } 114 115 type mockBlockStoreIterator struct { 116 mock.Mock 117 } 118 119 func (m *mockBlockStoreIterator) Next() (cl.QueryResult, error) { 120 args := m.Called() 121 return args.Get(0), args.Error(1) 122 } 123 124 func (m *mockBlockStoreIterator) Close() { 125 m.Called() 126 } 127 128 func TestInitialization(t *testing.T) { 129 tev, fl := initialize(t) 130 defer tev.tearDown() 131 132 assert.Equal(t, uint64(1), fl.Height(), "Block height should be 1") 133 134 block := blockledger.GetBlock(fl, 0) 135 assert.NotNil(t, block, "Error retrieving genesis block") 136 assert.Equal(t, protoutil.BlockHeaderHash(genesisBlock.Header), protoutil.BlockHeaderHash(block.Header), "Block hashes did no match") 137 } 138 139 func TestReinitialization(t *testing.T) { 140 tev, ledger1 := initialize(t) 141 defer tev.tearDown() 142 143 // create a block to add to the ledger 144 envelope := getSampleEnvelopeWithSignatureHeader() 145 b1 := blockledger.CreateNextBlock(ledger1, []*cb.Envelope{envelope}) 146 147 // add the block to the ledger 148 ledger1.Append(b1) 149 150 fl, err := tev.flf.GetOrCreate("testchannelid") 151 ledger1, ok := fl.(*FileLedger) 152 assert.NoError(t, err, "Expected to successfully get test channel") 153 assert.Equal(t, 1, len(tev.flf.ChannelIDs()), "Exptected not new channel to be created") 154 assert.True(t, ok, "Exptected type assertion to succeed") 155 assert.Equal(t, uint64(2), ledger1.Height(), "Block height should be 2. Got %v", ledger1.Height()) 156 157 // shut down the ledger provider 158 tev.shutDown() 159 160 // re-initialize the ledger provider (not the test ledger itself!) 161 provider2, err := New(tev.location, &disabled.Provider{}) 162 assert.NoError(t, err) 163 164 // assert expected ledgers exist 165 channels := provider2.ChannelIDs() 166 assert.Equal(t, 1, len(channels), "Should have recovered the channel") 167 168 // get the existing test channel ledger 169 ledger2, err := provider2.GetOrCreate(channels[0]) 170 assert.NoError(t, err, "Unexpected error: %s", err) 171 172 fl = ledger2.(*FileLedger) 173 assert.Equal(t, uint64(2), fl.Height(), "Block height should be 2. Got %v", fl.Height()) 174 175 block := blockledger.GetBlock(fl, 1) 176 assert.NotNil(t, block, "Error retrieving block 1") 177 assert.Equal(t, protoutil.BlockHeaderHash(b1.Header), protoutil.BlockHeaderHash(block.Header), "Block hashes did no match") 178 } 179 180 func TestAddition(t *testing.T) { 181 tev, fl := initialize(t) 182 defer tev.tearDown() 183 info, _ := fl.blockStore.GetBlockchainInfo() 184 prevHash := info.CurrentBlockHash 185 envelope := getSampleEnvelopeWithSignatureHeader() 186 b1 := blockledger.CreateNextBlock(fl, []*cb.Envelope{envelope}) 187 fl.Append(b1) 188 assert.Equal(t, uint64(2), fl.Height(), "Block height should be 2") 189 190 block := blockledger.GetBlock(fl, 1) 191 assert.NotNil(t, block, "Error retrieving genesis block") 192 assert.Equal(t, prevHash, block.Header.PreviousHash, "Block hashes did no match") 193 } 194 195 func TestRetrieval(t *testing.T) { 196 tev, fl := initialize(t) 197 defer tev.tearDown() 198 envelope := getSampleEnvelopeWithSignatureHeader() 199 b1 := blockledger.CreateNextBlock(fl, []*cb.Envelope{envelope}) 200 fl.Append(b1) 201 it, num := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Oldest{}}) 202 defer it.Close() 203 assert.Zero(t, num, "Expected genesis block iterator, but got %d", num) 204 205 block, status := it.Next() 206 assert.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the genesis block") 207 assert.Zero(t, block.Header.Number, "Expected to successfully retrieve the genesis block") 208 209 block, status = it.Next() 210 assert.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the second block") 211 assert.Equal( 212 t, 213 uint64(1), 214 block.Header.Number, 215 "Expected to successfully retrieve the second block but got block number %d", block.Header.Number) 216 } 217 218 func TestBlockedRetrieval(t *testing.T) { 219 tev, fl := initialize(t) 220 defer tev.tearDown() 221 it, num := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 1}}}) 222 defer it.Close() 223 if num != 1 { 224 t.Fatalf("Expected block iterator at 1, but got %d", num) 225 } 226 assert.Equal(t, uint64(1), num, "Expected block iterator at 1, but got %d", num) 227 228 envelope := getSampleEnvelopeWithSignatureHeader() 229 b1 := blockledger.CreateNextBlock(fl, []*cb.Envelope{envelope}) 230 fl.Append(b1) 231 232 block, status := it.Next() 233 assert.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the second block") 234 assert.Equal( 235 t, 236 uint64(1), 237 block.Header.Number, 238 "Expected to successfully retrieve the second block but got block number %d", block.Header.Number) 239 240 b2 := blockledger.CreateNextBlock(fl, []*cb.Envelope{envelope}) 241 fl.Append(b2) 242 243 block, status = it.Next() 244 assert.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the third block") 245 assert.Equal(t, uint64(2), block.Header.Number, "Expected to successfully retrieve the third block") 246 } 247 248 func TestBlockstoreError(t *testing.T) { 249 // Since this test only ensures failed GetBlockchainInfo 250 // is properly handled. We don't bother creating fully 251 // legit ledgers here (without genesis block). 252 { 253 fl := &FileLedger{ 254 blockStore: &mockBlockStore{ 255 blockchainInfo: nil, 256 getBlockchainInfoError: fmt.Errorf("Error getting blockchain info"), 257 }, 258 signal: make(chan struct{}), 259 } 260 assert.Panics( 261 t, 262 func() { 263 fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Newest{}}) 264 }, 265 "Expected Iterator() to panic if blockstore operation fails") 266 267 assert.Panics( 268 t, 269 func() { fl.Height() }, 270 "Expected Height() to panic if blockstore operation fails ") 271 } 272 273 { 274 fl := &FileLedger{ 275 blockStore: &mockBlockStore{ 276 blockchainInfo: &cb.BlockchainInfo{Height: uint64(1)}, 277 getBlockchainInfoError: nil, 278 retrieveBlockByNumberError: fmt.Errorf("Error retrieving block by number"), 279 }, 280 signal: make(chan struct{}), 281 } 282 it, _ := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 42}}}) 283 defer it.Close() 284 assert.IsType( 285 t, 286 &blockledger.NotFoundErrorIterator{}, 287 it, 288 "Expected Not Found Error if seek number is greater than ledger height") 289 } 290 291 { 292 resultsIterator := &mockBlockStoreIterator{} 293 resultsIterator.On("Next").Return(nil, errors.New("a mocked error")) 294 resultsIterator.On("Close").Return() 295 fl := &FileLedger{ 296 blockStore: &mockBlockStore{ 297 blockchainInfo: &cb.BlockchainInfo{Height: uint64(1)}, 298 getBlockchainInfoError: nil, 299 retrieveBlockByNumberError: fmt.Errorf("Error retrieving block by number"), 300 resultsIterator: resultsIterator, 301 }, 302 signal: make(chan struct{}), 303 } 304 it, _ := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Oldest{}}) 305 defer it.Close() 306 _, status := it.Next() 307 assert.Equal(t, cb.Status_SERVICE_UNAVAILABLE, status, "Expected service unavailable error") 308 } 309 } 310 311 func getSampleEnvelopeWithSignatureHeader() *cb.Envelope { 312 nonce := protoutil.CreateNonceOrPanic() 313 sighdr := &cb.SignatureHeader{Nonce: nonce} 314 sighdrBytes := protoutil.MarshalOrPanic(sighdr) 315 316 header := &cb.Header{SignatureHeader: sighdrBytes} 317 payload := &cb.Payload{Header: header} 318 payloadBytes := protoutil.MarshalOrPanic(payload) 319 return &cb.Envelope{Payload: payloadBytes} 320 }