Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LEDGER] tests for commit hashes with rollback and reset #1221

Merged
merged 1 commit into from
May 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion core/ledger/kvledger/tests/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (c *client) addPostOrderTx(txid string, customTxType common.HeaderType) *tx
}

// simulateDeployTx mimics a transction that deploys a chaincode. This in turn calls the function 'simulateDataTx'
// with supplying the simulation logic that mimics the inoke funciton of 'lscc' for the ledger tests
// with supplying the simulation logic that mimics the invoke function of 'lscc' for the ledger tests
func (c *client) simulateDeployTx(ccName string, collConfs []*collConf) *txAndPvtdata {
ccData := &ccprovider.ChaincodeData{Name: ccName}
ccDataBytes, err := proto.Marshal(ccData)
Expand Down Expand Up @@ -105,6 +105,22 @@ func (c *client) causeMissingPvtData(txIndex uint64) {
c.simulatedTrans[txIndex].Pvtws = nil
}

func (c *client) retrieveCommittedBlocksAndPvtdata(startNum, endNum uint64) []*ledger.BlockAndPvtData {
data := []*ledger.BlockAndPvtData{}
for i := startNum; i <= endNum; i++ {
d, err := c.lgr.GetPvtDataAndBlockByNum(i, nil)
c.assert.NoError(err)
data = append(data, d)
}
return data
}

func (c *client) currentHeight() uint64 {
bcInfo, err := c.lgr.GetBlockchainInfo()
c.assert.NoError(err)
return bcInfo.Height
}

/////////////////////// simulator wrapper functions ///////////////////////
type simulator struct {
ledger.TxSimulator
Expand Down
2 changes: 2 additions & 0 deletions core/ledger/kvledger/tests/sample_data_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,11 @@ func (d *sampleDataHelper) verifyBlockAndPvtdataUsingSubmittedData(h *testhelper
h.verifyBlockAndPvtData(uint64(8), nil, func(r *retrievedBlockAndPvtdata) {
r.sameBlockHeaderAndData(submittedBlk.Block)
r.containsValidationCode(0, protopeer.TxValidationCode_MVCC_READ_CONFLICT)
r.containsCommitHash()
})
}
}
h.verifyCommitHashExists()
}

func (d *sampleDataHelper) verifyGetTransactionByIDUsingSubmittedData(h *testhelper) {
Expand Down
2 changes: 1 addition & 1 deletion core/ledger/kvledger/tests/test_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
)

// testhelper embeds (1) a client, (2) a committer and (3) a verifier, all three operate on
// a ledger instance and add helping/resuable functionality on top of ledger apis that helps
// a ledger instance and add helping/reusable functionality on top of ledger apis that helps
// in avoiding the repeation in the actual tests code.
// the 'client' adds value to the simulation relation apis, the 'committer' helps in cutting the
// next block and committing the block, and finally, the verifier helps in veryfying that the
Expand Down
Binary file not shown.
166 changes: 166 additions & 0 deletions core/ledger/kvledger/tests/v1x_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,21 @@ import (
"testing"
"time"

"github.com/hyperledger/fabric-protos-go/common"
protopeer "github.com/hyperledger/fabric-protos-go/peer"
"github.com/hyperledger/fabric/common/ledger/testutil"
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/core/ledger/kvledger"
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb/statecouchdb"
"github.com/hyperledger/fabric/core/ledger/mock"
"github.com/hyperledger/fabric/protoutil"
"github.com/stretchr/testify/require"
)

// Test data used in the tests in this file was generated by v1.1 code https://gerrit.hyperledger.org/r/#/c/22749/6/core/ledger/kvledger/tests/v11_generate_test.go@22
// Folder, "testdata/v11/sample_ledgers" contains the data that was generated before commit hash feature was added.
// Folder, "testdata/v11/sample_ledgers_with_commit_hashes" contains the data that was generated after commit hash feature was added.

// TestV11 tests that a ledgersData folder created by v1.1 can be used with future releases after upgrading dbs.
// 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
func TestV11(t *testing.T) {
Expand All @@ -49,6 +55,166 @@ func TestV11(t *testing.T) {
h1, h2 = env.newTestHelperOpenLgr("ledger1", t), env.newTestHelperOpenLgr("ledger2", t)
dataHelper.verify(h1)
dataHelper.verify(h2)

h1.verifyCommitHashNotExists()
h2.verifyCommitHashNotExists()
h1.simulateDataTx("txid1_with_new_binary", func(s *simulator) {
s.setState("cc1", "new_key", "new_value")
})

// add a new block and the new block should not contain a commit hash
// because the previously committed block from 1.1 code did not contain commit hash
h1.cutBlockAndCommitLegacy()
h1.verifyCommitHashNotExists()
}

func TestV11CommitHashes(t *testing.T) {
testCases := []struct {
description string
v11SampleDataPath string
preResetCommitHashExists bool
resetFunc func(h *testhelper, ledgerFSRoot string)
postResetCommitHashExists bool
}{
{
"Reset (no existing CommitHash)",
"testdata/v11/sample_ledgers/ledgersData.zip",
false,
func(h *testhelper, ledgerFSRoot string) {
require.NoError(t, kvledger.ResetAllKVLedgers(ledgerFSRoot))
},
true,
},

{
"Rollback to genesis block (no existing CommitHash)",
"testdata/v11/sample_ledgers/ledgersData.zip",
false,
func(h *testhelper, ledgerFSRoot string) {
require.NoError(t, kvledger.RollbackKVLedger(ledgerFSRoot, h.lgrid, 0))
},
true,
},

{
"Rollback to block other than genesis block (no existing CommitHash)",
"testdata/v11/sample_ledgers/ledgersData.zip",
false,
func(h *testhelper, ledgerFSRoot string) {
require.NoError(t, kvledger.RollbackKVLedger(ledgerFSRoot, h.lgrid, h.currentHeight()/2+1))
},
false,
},

{
"Reset (existing CommitHash)",
"testdata/v11/sample_ledgers_with_commit_hashes/ledgersData.zip",
true,
func(h *testhelper, ledgerFSRoot string) {
require.NoError(t, kvledger.ResetAllKVLedgers(ledgerFSRoot))
},
true,
},

{
"Rollback to genesis block (existing CommitHash)",
"testdata/v11/sample_ledgers_with_commit_hashes/ledgersData.zip",
true,
func(h *testhelper, ledgerFSRoot string) {
require.NoError(t, kvledger.RollbackKVLedger(ledgerFSRoot, h.lgrid, 0))
},
true,
},

{
"Rollback to block other than genesis block (existing CommitHash)",
"testdata/v11/sample_ledgers_with_commit_hashes/ledgersData.zip",
true,
func(h *testhelper, ledgerFSRoot string) {
require.NoError(t, kvledger.RollbackKVLedger(ledgerFSRoot, h.lgrid, h.currentHeight()/2+1))
},
true,
},
}

for _, testCase := range testCases {
t.Run(
testCase.description,
func(t *testing.T) {
testV11CommitHashes(
t,
testCase.v11SampleDataPath,
testCase.preResetCommitHashExists,
testCase.resetFunc,
testCase.postResetCommitHashExists,
)
})
}
}

func testV11CommitHashes(t *testing.T,
v11DataPath string,
preResetCommitHashExists bool,
resetFunc func(*testhelper, string),
postResetCommitHashExists bool,
) {
env := newEnv(t)
defer env.cleanup()

ledgerFSRoot := env.initializer.Config.RootFSPath
// pass false so that 'ledgersData' directory will not be created when unzipped to ledgerFSRoot
require.NoError(t, testutil.Unzip(v11DataPath, ledgerFSRoot, false))

require.NoError(t, kvledger.UpgradeDBs(env.initializer.Config))
rebuildable := rebuildableStatedb | rebuildableBookkeeper | rebuildableConfigHistory | rebuildableHistoryDB | rebuildableBlockIndex
env.verifyRebuilableDoesNotExist(rebuildable)

env.initLedgerMgmt()
h := env.newTestHelperOpenLgr("ledger1", t)
blocksAndPvtData := h.retrieveCommittedBlocksAndPvtdata(0, h.currentHeight()-1)
if preResetCommitHashExists {
h.verifyCommitHashExists()
} else {
h.verifyCommitHashNotExists()
}

env.closeLedgerMgmt()
resetFunc(h, ledgerFSRoot)
env.initLedgerMgmt()

h = env.newTestHelperOpenLgr("ledger1", t)
for i := int(h.currentHeight()); i < len(blocksAndPvtData); i++ {
d := blocksAndPvtData[i]
// add metadata slot for commit hash, as this would have be missing in the blocks from 1.1 prior to this feature
for len(d.Block.Metadata.Metadata) < int(common.BlockMetadataIndex_COMMIT_HASH)+1 {
d.Block.Metadata.Metadata = append(d.Block.Metadata.Metadata, []byte{})
}
// set previous block hash, as this is not present in the test blocks from 1.1
d.Block.Header.PreviousHash = protoutil.BlockHeaderHash(blocksAndPvtData[i-1].Block.Header)
require.NoError(t, h.lgr.CommitLegacy(d, &ledger.CommitOptions{FetchPvtDataFromLedger: true}))
}

if postResetCommitHashExists {
h.verifyCommitHashExists()
} else {
h.verifyCommitHashNotExists()
}

bcInfo, err := h.lgr.GetBlockchainInfo()
require.NoError(t, err)
h.committer.blkgen.lastNum = bcInfo.Height - 1
h.committer.blkgen.lastHash = bcInfo.CurrentBlockHash

h.simulateDataTx("txid1_with_new_binary", func(s *simulator) {
s.setState("cc1", "new_key", "new_value")
})
h.cutBlockAndCommitLegacy()

if postResetCommitHashExists {
h.verifyCommitHashExists()
} else {
h.verifyCommitHashNotExists()
}
}

// TestV13WithStateCouchdb tests that a ledgersData folder and couchdb data created by v1.3 can be read by latest fabric version after upgrading dbs.
Expand Down
52 changes: 41 additions & 11 deletions core/ledger/kvledger/tests/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,24 @@ func (v *verifier) verifyHistory(ns, key string, expectedVals []string) {
v.assert.Equal(expectedVals, historyValues)
}

func (v *verifier) verifyCommitHashExists() {
bcInfo, err := v.lgr.GetBlockchainInfo()
v.assert.NoError(err)
b, err := v.lgr.GetPvtDataAndBlockByNum(bcInfo.Height-1, nil)
v.assert.NoError(err)
r := &retrievedBlockAndPvtdata{BlockAndPvtData: b, assert: v.assert}
r.containsCommitHash()
}

func (v *verifier) verifyCommitHashNotExists() {
bcInfo, err := v.lgr.GetBlockchainInfo()
v.assert.NoError(err)
b, err := v.lgr.GetPvtDataAndBlockByNum(bcInfo.Height-1, nil)
v.assert.NoError(err)
r := &retrievedBlockAndPvtdata{BlockAndPvtData: b, assert: v.assert}
r.notContainCommitHash()
}

//////////// structs used by verifier //////////////////////////////////////////////////////////////
type expectedCollConfInfo struct {
committingBlockNum uint64
Expand Down Expand Up @@ -200,18 +218,13 @@ func (r *retrievedBlockAndPvtdata) sameMetadata(expectedBlock *common.Block) {
expectedMetadata := expectedBlock.Metadata.Metadata
r.assert.Equal(len(expectedMetadata), len(retrievedMetadata))
for i := 0; i < len(expectedMetadata); i++ {
if i == int(common.BlockMetadataIndex_COMMIT_HASH) {
// in order to compare the exact hash value, we need to duplicate the
// production code in this test too, so skipping this match
continue
}
if len(expectedMetadata[i])+len(retrievedMetadata[i]) != 0 {
if i != int(common.BlockMetadataIndex_COMMIT_HASH) {
r.assert.Equal(expectedMetadata[i], retrievedMetadata[i])
} else {
// in order to compare the exact hash value, we need to duplicate the
// production code in this test too (which is not recommended).
commitHash := &common.Metadata{}
err := proto.Unmarshal(retrievedMetadata[common.BlockMetadataIndex_COMMIT_HASH],
commitHash)
r.assert.NoError(err)
r.assert.Equal(len(commitHash.Value), 32)
}
r.assert.Equal(expectedMetadata[i], retrievedMetadata[i])
}
}
}
Expand All @@ -230,3 +243,20 @@ func (r *retrievedBlockAndPvtdata) samePvtdata(expectedPvtdata map[uint64]*ledge
r.assert.True(proto.Equal(pvtData.WriteSet, actualPvtData.WriteSet))
}
}

func (r *retrievedBlockAndPvtdata) containsCommitHash() {
commitHash := &common.Metadata{}
spew.Dump(r.Block.Metadata)
err := proto.Unmarshal(
r.Block.Metadata.Metadata[common.BlockMetadataIndex_COMMIT_HASH],
commitHash,
)
r.assert.NoError(err)
r.assert.Equal(len(commitHash.Value), 32)
}

func (r *retrievedBlockAndPvtdata) notContainCommitHash() {
exists := len(r.Block.Metadata.Metadata) >= int(common.BlockMetadataIndex_COMMIT_HASH)+1 &&
len(r.Block.Metadata.Metadata[common.BlockMetadataIndex_COMMIT_HASH]) > 0
r.assert.False(exists)
}