github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+incompatible/core/ledger/kvledger/tests/v1x_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package tests
     8  
     9  import (
    10  	"fmt"
    11  	"os"
    12  	"path"
    13  	"path/filepath"
    14  	"testing"
    15  	"time"
    16  
    17  	protopeer "github.com/hyperledger/fabric-protos-go/peer"
    18  	"github.com/hyperledger/fabric/common/ledger/testutil"
    19  	"github.com/hyperledger/fabric/core/ledger/kvledger"
    20  	"github.com/hyperledger/fabric/core/ledger/mock"
    21  	"github.com/hyperledger/fabric/core/ledger/util/couchdb"
    22  	"github.com/stretchr/testify/require"
    23  )
    24  
    25  // TestV11 tests that a ledgersData folder created by v1.1 can be used with future releases in a backward compatible way
    26  // The test data was generated by v1.1 code https://github.com/hyperledger/fabric/blob/release-1.1/core/ledger/kvledger/tests/v11_generate_test.go#L22
    27  func TestV11(t *testing.T) {
    28  	env := newEnv(t)
    29  	defer env.cleanup()
    30  
    31  	ledgerFSRoot := env.initializer.Config.RootFSPath
    32  	// pass false so that 'ledgersData' directory will not be created when unzipped to ledgerFSRoot
    33  	require.NoError(t, testutil.Unzip("testdata/v11/sample_ledgers/ledgersData.zip", ledgerFSRoot, false))
    34  
    35  	// verify init ledger panic and drop the corresponding db after each panic
    36  	checkInitLedgerPanicAndDropDBs(t, env, ledgerFSRoot, nil)
    37  
    38  	t.Logf("All stores are reformatted/dropped, now ledgermgmt initialization should not panic")
    39  	env.initLedgerMgmt()
    40  
    41  	h1, h2 := env.newTestHelperOpenLgr("ledger1", t), env.newTestHelperOpenLgr("ledger2", t)
    42  	dataHelper := &v1xSampleDataHelper{sampleDataVersion: "v1.1", t: t}
    43  
    44  	dataHelper.verify(h1)
    45  	dataHelper.verify(h2)
    46  
    47  	env.closeAllLedgersAndDrop(rebuildableStatedb + rebuildableBlockIndex + rebuildableConfigHistory + rebuildableHistoryDB)
    48  	h1, h2 = env.newTestHelperOpenLgr("ledger1", t), env.newTestHelperOpenLgr("ledger2", t)
    49  	dataHelper.verify(h1)
    50  	dataHelper.verify(h2)
    51  }
    52  
    53  // TestV13WithStateCouchdb tests that a ledgersData folder and couchdb data created by v1.3 can be read by latest fabric version in a backward compatible way
    54  // The test data was generated by v1.3 code https://gerrit.hyperledger.org/r/#/c/fabric/+/34078/3/core/ledger/kvledger/tests/v13_generate_test.go@60
    55  func TestV13WithStateCouchdb(t *testing.T) {
    56  	env := newEnv(t)
    57  	defer env.cleanup()
    58  
    59  	ledgerFSRoot := env.initializer.Config.RootFSPath
    60  	// pass false so that 'ledgersData' directory will not be created when unzipped to ledgerFSRoot
    61  	require.NoError(t, testutil.Unzip("testdata/v13_statecouchdb/sample_ledgers/ledgersData.zip", ledgerFSRoot, false))
    62  
    63  	// unzip couchdb data to prepare the mount dir
    64  	couchdbDataUnzipDir := filepath.Join(ledgerFSRoot, "couchdbData")
    65  	require.NoError(t, os.Mkdir(couchdbDataUnzipDir, os.ModePerm))
    66  	require.NoError(t, testutil.Unzip("testdata/v13_statecouchdb/sample_ledgers/couchdbData.zip", couchdbDataUnzipDir, false))
    67  
    68  	// prepare the local.d mount dir to overwrite the number of shards and nodes so that they match the couchdb data generated from v1.3
    69  	localdHostDir := filepath.Join(ledgerFSRoot, "local.d")
    70  	require.NoError(t, os.MkdirAll(localdHostDir, os.ModePerm))
    71  	testutil.CopyDir("testdata/v13_statecouchdb/couchdb_etc/local.d", localdHostDir, true)
    72  
    73  	// start couchdb using couchdbDataUnzipDir and localdHostDir as mount dirs
    74  	couchAddress, cleanup := couchDBSetup(t, couchdbDataUnzipDir, localdHostDir)
    75  	defer cleanup()
    76  
    77  	// set required config data to use state couchdb
    78  	couchdbConfig := &couchdb.Config{
    79  		Address:             couchAddress,
    80  		Username:            "",
    81  		Password:            "",
    82  		MaxRetries:          3,
    83  		MaxRetriesOnStartup: 3,
    84  		RequestTimeout:      10 * time.Second,
    85  		RedoLogPath:         filepath.Join(ledgerFSRoot, "couchdbRedoLogs"),
    86  	}
    87  	env.initializer.Config.StateDBConfig.StateDatabase = "CouchDB"
    88  	env.initializer.Config.StateDBConfig.CouchDB = couchdbConfig
    89  	env.initializer.HealthCheckRegistry = &mock.HealthCheckRegistry{}
    90  	env.initializer.ChaincodeLifecycleEventProvider = &mock.ChaincodeLifecycleEventProvider{}
    91  
    92  	// verify init ledger panic and drop the corresponding db after each panic
    93  	checkInitLedgerPanicAndDropDBs(t, env, ledgerFSRoot, couchdbConfig)
    94  
    95  	t.Logf("All stores are reformatted/dropped, now ledgermgmt initialization should not panic")
    96  	env.initLedgerMgmt()
    97  
    98  	h1, h2 := env.newTestHelperOpenLgr("ledger1", t), env.newTestHelperOpenLgr("ledger2", t)
    99  	dataHelper := &v1xSampleDataHelper{sampleDataVersion: "v1.3", t: t}
   100  
   101  	dataHelper.verify(h1)
   102  	dataHelper.verify(h2)
   103  
   104  	// drop couchDB and other rebuildable dbs to test db rebuild
   105  	dropCouchDBs(t, couchdbConfig)
   106  	env.closeAllLedgersAndDrop(rebuildableBlockIndex + rebuildableConfigHistory + rebuildableHistoryDB)
   107  	h1, h2 = env.newTestHelperOpenLgr("ledger1", t), env.newTestHelperOpenLgr("ledger2", t)
   108  	dataHelper.verify(h1)
   109  	dataHelper.verify(h2)
   110  }
   111  
   112  func checkInitLedgerPanicAndDropDBs(t *testing.T, env *env, ledgerFSRoot string, couchdbConfig *couchdb.Config) {
   113  	t.Logf("verifying that a panic occurs because idStore has old format and then reformat the idstore to proceed")
   114  	idStorePath := kvledger.LedgerProviderPath(ledgerFSRoot)
   115  	require.PanicsWithValue(
   116  		t,
   117  		fmt.Sprintf("Error in instantiating ledger provider: unexpected format. db info = [leveldb for channel-IDs at [%s]], data format = [], expected format = [2.0]",
   118  			idStorePath),
   119  		func() { env.initLedgerMgmt() },
   120  		"A panic should occur because idstore is in format 1.x",
   121  	)
   122  	require.NoError(t, kvledger.UpgradeIDStoreFormat(ledgerFSRoot))
   123  
   124  	t.Logf("verifying that a panic occurs because blockstore index has old format and then drop the idstore to proceed")
   125  	blkIndexPath := path.Join(kvledger.BlockStorePath(ledgerFSRoot), "index")
   126  	require.PanicsWithValue(
   127  		t,
   128  		fmt.Sprintf("Error in instantiating ledger provider: unexpected format. db info = [leveldb at [%s]], data format = [], expected format = [2.0]",
   129  			blkIndexPath),
   130  		func() { env.initLedgerMgmt() },
   131  		"A panic should occur because block store index is in format 1.x",
   132  	)
   133  	require.NoError(t, os.RemoveAll(blkIndexPath))
   134  
   135  	t.Logf("verifying that a panic occurs because historydb has old format and then drop the historydb to proceed")
   136  	historyDBPath := kvledger.HistoryDBPath(ledgerFSRoot)
   137  	require.PanicsWithValue(
   138  		t,
   139  		fmt.Sprintf("Error in instantiating ledger provider: unexpected format. db info = [leveldb at [%s]], data format = [], expected format = [2.0]",
   140  			historyDBPath),
   141  		func() { env.initLedgerMgmt() },
   142  		"A panic should occur because history is in format 1.x",
   143  	)
   144  	require.NoError(t, os.RemoveAll(historyDBPath))
   145  
   146  	if couchdbConfig == nil {
   147  		t.Logf("verifying that a panic occurs because stateleveldb has old format and then drop the statedb to proceed")
   148  		stateLevelDBPath := kvledger.StateDBPath(ledgerFSRoot)
   149  		require.PanicsWithValue(
   150  			t,
   151  			fmt.Sprintf(
   152  				"Error in instantiating ledger provider: unexpected format. db info = [leveldb at [%s]], data format = [], expected format = [2.0]",
   153  				stateLevelDBPath,
   154  			),
   155  			func() { env.initLedgerMgmt() },
   156  			"A panic should occur because statedb is in format 1.x",
   157  		)
   158  		require.NoError(t, os.RemoveAll(stateLevelDBPath))
   159  	} else {
   160  		t.Logf("verifying that a panic occurs because statecouchdb has old format and then drop the statedb to proceed")
   161  		require.PanicsWithValue(
   162  			t,
   163  			fmt.Sprintf(
   164  				"Error in instantiating ledger provider: unexpected format. db info = [CouchDB for state database], data format = [], expected format = [2.0]"),
   165  			func() { env.initLedgerMgmt() },
   166  			"A panic should occur because statedb is in format 1.x",
   167  		)
   168  		dropCouchDBs(t, couchdbConfig)
   169  	}
   170  }
   171  
   172  // v1xSampleDataHelper provides a set of functions to verify the ledger (after upgraded to latest data format).
   173  // It verifies the ledger under the assumption that the ledger was generated by the specific generation code from v1.1 or v1.3.
   174  // For v1.1, the sample ledger data was generated by https://github.com/hyperledger/fabric/blob/release-1.1/core/ledger/kvledger/tests/v11_generate_test.go#L22
   175  // This generate function constructed two ledgers and populateed the ledgers using this code
   176  // (https://github.com/hyperledger/fabric/blob/release-1.1/core/ledger/kvledger/tests/sample_data_helper.go#L55)
   177  // For v1.3, the sample ledger data was generated by CR (https://gerrit.hyperledger.org/r/#/c/fabric/+/34078/3/core/ledger/kvledger/tests/v13_generate_test.go@60).
   178  // This generate function constructed two ledgers and populated the ledgers using this code
   179  // (https://github.com/hyperledger/fabric/blob/release-1.3/core/ledger/kvledger/tests/sample_data_helper.go#L55)
   180  type v1xSampleDataHelper struct {
   181  	sampleDataVersion string
   182  	t                 *testing.T
   183  }
   184  
   185  func (d *v1xSampleDataHelper) verify(h *testhelper) {
   186  	d.verifyState(h)
   187  	d.verifyBlockAndPvtdata(h)
   188  	d.verifyGetTransactionByID(h)
   189  	d.verifyConfigHistory(h)
   190  	d.verifyHistory(h)
   191  }
   192  
   193  func (d *v1xSampleDataHelper) verifyState(h *testhelper) {
   194  	lgrid := h.lgrid
   195  	h.verifyPubState("cc1", "key1", d.sampleVal("value13", lgrid))
   196  	h.verifyPubState("cc1", "key2", "")
   197  	h.verifyPvtState("cc1", "coll1", "key3", d.sampleVal("value14", lgrid))
   198  	h.verifyPvtState("cc1", "coll1", "key4", "")
   199  	h.verifyPvtState("cc1", "coll2", "key3", d.sampleVal("value09", lgrid))
   200  	h.verifyPvtState("cc1", "coll2", "key4", d.sampleVal("value10", lgrid))
   201  
   202  	h.verifyPubState("cc2", "key1", d.sampleVal("value03", lgrid))
   203  	h.verifyPubState("cc2", "key2", d.sampleVal("value04", lgrid))
   204  	h.verifyPvtState("cc2", "coll1", "key3", d.sampleVal("value07", lgrid))
   205  	h.verifyPvtState("cc2", "coll1", "key4", d.sampleVal("value08", lgrid))
   206  	h.verifyPvtState("cc2", "coll2", "key3", d.sampleVal("value11", lgrid))
   207  	h.verifyPvtState("cc2", "coll2", "key4", d.sampleVal("value12", lgrid))
   208  }
   209  
   210  func (d *v1xSampleDataHelper) verifyHistory(h *testhelper) {
   211  	lgrid := h.lgrid
   212  	expectedHistoryCC1Key1 := []string{
   213  		d.sampleVal("value13", lgrid),
   214  		d.sampleVal("value01", lgrid),
   215  	}
   216  	h.verifyHistory("cc1", "key1", expectedHistoryCC1Key1)
   217  }
   218  
   219  func (d *v1xSampleDataHelper) verifyConfigHistory(h *testhelper) {
   220  	lgrid := h.lgrid
   221  	h.verifyMostRecentCollectionConfigBelow(10, "cc1",
   222  		&expectedCollConfInfo{5, d.sampleCollConf2(lgrid, "cc1")})
   223  
   224  	h.verifyMostRecentCollectionConfigBelow(5, "cc1",
   225  		&expectedCollConfInfo{3, d.sampleCollConf1(lgrid, "cc1")})
   226  
   227  	h.verifyMostRecentCollectionConfigBelow(10, "cc2",
   228  		&expectedCollConfInfo{5, d.sampleCollConf2(lgrid, "cc2")})
   229  
   230  	h.verifyMostRecentCollectionConfigBelow(5, "cc2",
   231  		&expectedCollConfInfo{3, d.sampleCollConf1(lgrid, "cc2")})
   232  }
   233  
   234  func (d *v1xSampleDataHelper) verifyConfigHistoryDoesNotExist(h *testhelper) {
   235  	h.verifyMostRecentCollectionConfigBelow(10, "cc1", nil)
   236  	h.verifyMostRecentCollectionConfigBelow(10, "cc2", nil)
   237  }
   238  
   239  func (d *v1xSampleDataHelper) verifyBlockAndPvtdata(h *testhelper) {
   240  	lgrid := h.lgrid
   241  	h.verifyBlockAndPvtData(2, nil, func(r *retrievedBlockAndPvtdata) {
   242  		r.hasNumTx(2)
   243  		r.hasNoPvtdata()
   244  	})
   245  
   246  	h.verifyBlockAndPvtData(4, nil, func(r *retrievedBlockAndPvtdata) {
   247  		r.hasNumTx(2)
   248  		r.pvtdataShouldContain(0, "cc1", "coll1", "key3", d.sampleVal("value05", lgrid))
   249  		r.pvtdataShouldContain(1, "cc2", "coll1", "key3", d.sampleVal("value07", lgrid))
   250  	})
   251  }
   252  
   253  func (d *v1xSampleDataHelper) verifyGetTransactionByID(h *testhelper) {
   254  	h.verifyTxValidationCode("txid7", protopeer.TxValidationCode_VALID)
   255  	h.verifyTxValidationCode("txid8", protopeer.TxValidationCode_MVCC_READ_CONFLICT)
   256  }
   257  
   258  func (d *v1xSampleDataHelper) sampleVal(val, ledgerid string) string {
   259  	return fmt.Sprintf("%s:%s", val, ledgerid)
   260  }
   261  
   262  func (d *v1xSampleDataHelper) sampleCollConf1(ledgerid, ccName string) []*collConf {
   263  	switch d.sampleDataVersion {
   264  	case "v1.1":
   265  		return []*collConf{
   266  			{name: "coll1", members: []string{"org1", "org2"}},
   267  			{name: ledgerid, members: []string{"org1", "org2"}},
   268  			{name: ccName, members: []string{"org1", "org2"}},
   269  		}
   270  	case "v1.3":
   271  		return []*collConf{
   272  			{name: "coll1"},
   273  			{name: ledgerid},
   274  			{name: ccName},
   275  		}
   276  	default:
   277  		// should not happen
   278  		require.Failf(d.t, "sample data version %s is wrong", d.sampleDataVersion)
   279  		return nil
   280  	}
   281  }
   282  
   283  func (d *v1xSampleDataHelper) sampleCollConf2(ledgerid string, ccName string) []*collConf {
   284  	switch d.sampleDataVersion {
   285  	case "v1.1":
   286  		return []*collConf{
   287  			{name: "coll1", members: []string{"org1", "org2"}},
   288  			{name: "coll2", members: []string{"org1", "org2"}},
   289  			{name: ledgerid, members: []string{"org1", "org2"}},
   290  			{name: ccName, members: []string{"org1", "org2"}},
   291  		}
   292  	case "v1.3":
   293  		return []*collConf{
   294  			{name: "coll1"},
   295  			{name: "coll2"},
   296  			{name: ledgerid},
   297  			{name: ccName},
   298  		}
   299  	default:
   300  		// should not happen
   301  		require.Failf(d.t, "sample data version %s is wrong", d.sampleDataVersion)
   302  		return nil
   303  	}
   304  }