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  }