github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/orderer/ledger/json/impl_test.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package jsonledger 18 19 import ( 20 "io/ioutil" 21 "os" 22 "testing" 23 "time" 24 25 "github.com/hyperledger/fabric/common/configtx/tool/provisional" 26 "github.com/hyperledger/fabric/orderer/ledger" 27 cb "github.com/hyperledger/fabric/protos/common" 28 ab "github.com/hyperledger/fabric/protos/orderer" 29 30 logging "github.com/op/go-logging" 31 "github.com/stretchr/testify/assert" 32 ) 33 34 var genesisBlock = cb.NewBlock(0, nil) 35 36 func init() { 37 logging.SetLevel(logging.DEBUG, "") 38 } 39 40 type testEnv struct { 41 t *testing.T 42 location string 43 } 44 45 func initialize(t *testing.T) (*testEnv, *jsonLedger) { 46 name, err := ioutil.TempDir("", "hyperledger_fabric") 47 if err != nil { 48 t.Fatalf("Error creating temp dir: %s", err) 49 } 50 flf := New(name).(*jsonLedgerFactory) 51 fl, err := flf.GetOrCreate(provisional.TestChainID) 52 if err != nil { 53 panic(err) 54 } 55 56 fl.Append(genesisBlock) 57 return &testEnv{location: name, t: t}, fl.(*jsonLedger) 58 } 59 60 func (tev *testEnv) tearDown() { 61 err := os.RemoveAll(tev.location) 62 if err != nil { 63 tev.t.Fatalf("Error tearing down env: %s", err) 64 } 65 } 66 67 func TestInitialization(t *testing.T) { 68 tev, fl := initialize(t) 69 defer tev.tearDown() 70 assert.Equal(t, uint64(1), fl.height, "Block height should be 1") 71 72 block, found := fl.readBlock(0) 73 assert.NotNil(t, block, "Error retrieving genesis block") 74 assert.True(t, found, "Error retrieving genesis block") 75 assert.Equal(t, fl.lastHash, block.Header.Hash(), "Block hashes did no match") 76 77 } 78 79 func TestReinitialization(t *testing.T) { 80 tev, ofl := initialize(t) 81 defer tev.tearDown() 82 ofl.Append(ledger.CreateNextBlock(ofl, []*cb.Envelope{&cb.Envelope{Payload: []byte("My Data")}})) 83 flf := New(tev.location) 84 chains := flf.ChainIDs() 85 assert.Len(t, chains, 1, "Should have recovered the chain") 86 87 tfl, err := flf.GetOrCreate(chains[0]) 88 assert.Nil(t, err, "Unexpected error: %s", err) 89 90 fl := tfl.(*jsonLedger) 91 assert.Equal(t, uint64(2), fl.height, "Block height should be 2") 92 93 block, found := fl.readBlock(1) 94 assert.NotNil(t, block, "Error retrieving block") 95 assert.True(t, found, "Error retrieving block") 96 assert.Equal(t, fl.lastHash, block.Header.Hash(), "Block hashes did no match") 97 } 98 99 func TestMultiReinitialization(t *testing.T) { 100 tev, _ := initialize(t) 101 defer tev.tearDown() 102 flf := New(tev.location) 103 104 _, err := flf.GetOrCreate("foo") 105 assert.Nil(t, err, "Error creating chain") 106 107 _, err = flf.GetOrCreate("bar") 108 assert.Nil(t, err, "Error creating chain") 109 110 flf = New(tev.location) 111 assert.Len(t, flf.ChainIDs(), 3, "Should have recovered the chains") 112 } 113 114 func TestAddition(t *testing.T) { 115 tev, fl := initialize(t) 116 defer tev.tearDown() 117 prevHash := fl.lastHash 118 fl.Append(ledger.CreateNextBlock(fl, []*cb.Envelope{&cb.Envelope{Payload: []byte("My Data")}})) 119 assert.Equal(t, uint64(2), fl.height, "Block height should be 2") 120 121 block, found := fl.readBlock(1) 122 assert.NotNil(t, block, "Error retrieving genesis block") 123 assert.True(t, found, "Error retrieving genesis block") 124 assert.Equal(t, prevHash, block.Header.PreviousHash, "Block hashes did no match") 125 } 126 127 func TestRetrieval(t *testing.T) { 128 tev, fl := initialize(t) 129 defer tev.tearDown() 130 fl.Append(ledger.CreateNextBlock(fl, []*cb.Envelope{&cb.Envelope{Payload: []byte("My Data")}})) 131 it, num := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Oldest{}}) 132 assert.Equal(t, uint64(0), num, "Expected genesis block iterator, but got %d", num) 133 134 signal := it.ReadyChan() 135 select { 136 case <-signal: 137 default: 138 t.Fatalf("Should be ready for block read") 139 } 140 141 block, status := it.Next() 142 assert.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the genesis block") 143 assert.Equal(t, uint64(0), block.Header.Number, "Expected to successfully retrieve the genesis block") 144 145 signal = it.ReadyChan() 146 select { 147 case <-signal: 148 default: 149 t.Fatalf("Should still be ready for block read") 150 } 151 152 block, status = it.Next() 153 assert.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the second block") 154 assert.Equal(t, uint64(1), block.Header.Number, "Expected to successfully retrieve the second block but got block number %d", block.Header.Number) 155 } 156 157 // Without file lock in the implementation, this test is flaky due to 158 // a race condition of concurrent write and read of block file. With 159 // the fix, running this test repeatedly should not yield failure. 160 func TestRaceCondition(t *testing.T) { 161 tev, fl := initialize(t) 162 defer tev.tearDown() 163 164 it, _ := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 1}}}) 165 166 var block *cb.Block 167 var status cb.Status 168 169 complete := make(chan struct{}) 170 go func() { 171 block, status = it.Next() 172 close(complete) 173 }() 174 175 fl.Append(ledger.CreateNextBlock(fl, []*cb.Envelope{{Payload: []byte("My Data")}})) 176 <-complete 177 178 assert.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the block") 179 } 180 181 func TestBlockedRetrieval(t *testing.T) { 182 tev, fl := initialize(t) 183 defer tev.tearDown() 184 it, num := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 1}}}) 185 assert.Equal(t, uint64(1), num, "Expected block iterator at 1, but got %d", num) 186 187 signal := it.ReadyChan() 188 select { 189 case <-signal: 190 t.Fatalf("Should not be ready for block read") 191 default: 192 } 193 194 fl.Append(ledger.CreateNextBlock(fl, []*cb.Envelope{{Payload: []byte("My Data")}})) 195 select { 196 case <-signal: 197 default: 198 t.Fatalf("Should now be ready for block read") 199 } 200 201 block, status := it.Next() 202 assert.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the second block") 203 assert.Equal(t, uint64(1), block.Header.Number, "Expected to successfully retrieve the second block") 204 205 go func() { 206 // Add explicit sleep here to make sure `it.Next` is actually blocked waiting 207 // for new block. According to Golang sched, `it.Next()` is run before this 208 // goroutine, however it's not guaranteed to run till the channel operation 209 // we desire, due to I/O operation in the middle. Consider making the 210 // implementation more testable so we don't need to sleep here. 211 time.Sleep(100 * time.Millisecond) 212 fl.Append(ledger.CreateNextBlock(fl, []*cb.Envelope{{Payload: []byte("Another Data")}})) 213 }() 214 215 block, status = it.Next() 216 assert.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the third block") 217 assert.Equal(t, uint64(2), block.Header.Number, "Expected to successfully retrieve the third block") 218 } 219 220 func TestInvalidRetrieval(t *testing.T) { 221 tev, fl := initialize(t) 222 defer tev.tearDown() 223 224 it, num := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 2}}}) 225 assert.Equal(t, uint64(0), num, "Expected block number to be zero for invalid iterator") 226 227 _, status := it.Next() 228 assert.Equal(t, cb.Status_NOT_FOUND, status, "Expected status_NOT_FOUND for invalid iterator") 229 } 230 231 func TestBrokenBlockFile(t *testing.T) { 232 tev, fl := initialize(t) 233 defer tev.tearDown() 234 235 // Pollute block file so that unmarshalling would fail. 236 file, err := os.OpenFile(fl.blockFilename(0), os.O_RDWR, 0700) 237 assert.Nil(t, err, "Expected to successfully open block file") 238 239 _, err = file.WriteString("Hello, world!") 240 assert.Nil(t, err, "Expected to successfully write to block file") 241 242 assert.NoError(t, file.Close(), "Expected to successfully close block file") 243 244 it, num := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Oldest{}}) 245 assert.Equal(t, uint64(0), num, "Expected genesis block iterator, but got %d", num) 246 247 _, status := it.Next() 248 assert.Equal(t, cb.Status_SERVICE_UNAVAILABLE, status, "Expected reading the genesis block to fail") 249 } 250 251 func TestInvalidAddition(t *testing.T) { 252 tev, fl := initialize(t) 253 defer tev.tearDown() 254 255 // Append block with invalid number 256 { 257 block := ledger.CreateNextBlock(fl, []*cb.Envelope{{Payload: []byte("My Data")}}) 258 block.Header.Number++ 259 assert.Error(t, fl.Append(block), "Addition of block with invalid number should fail") 260 } 261 262 // Append block with invalid previousHash 263 { 264 block := ledger.CreateNextBlock(fl, []*cb.Envelope{{Payload: []byte("My Data")}}) 265 block.Header.PreviousHash = nil 266 assert.Error(t, fl.Append(block), "Addition of block with invalid previousHash should fail") 267 } 268 }