Skip to content

Commit 137bfcb

Browse files
committed
[FAB-10095] Add pvt data related tests
This CR introduces a test folder under kvledger that contains - Util code for making it easy to write tests that operate at the level of ledger APIs - Tests related to pvt data functions utilizing the above mentioned util code Change-Id: Ic16007cf2d6034249b0a5e10c0bcb0b800439dae Signed-off-by: manish <manish.sethi@gmail.com>
1 parent 38ad642 commit 137bfcb

File tree

11 files changed

+1195
-0
lines changed

11 files changed

+1195
-0
lines changed

core/ledger/kvledger/kv_ledger.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ func (l *kvLedger) recoverDBs() error {
140140
//recommitLostBlocks retrieves blocks in specified range and commit the write set to either
141141
//state DB or history DB or both
142142
func (l *kvLedger) recommitLostBlocks(firstBlockNum uint64, lastBlockNum uint64, recoverables ...recoverable) error {
143+
logger.Debugf("recommitLostBlocks() - firstBlockNum=%d, lastBlockNum=%d, recoverables=%#v", firstBlockNum, lastBlockNum, recoverables)
143144
var err error
144145
var blockAndPvtdata *ledger.BlockAndPvtData
145146
for blockNumber := firstBlockNum; blockNumber <= lastBlockNum; blockNumber++ {

core/ledger/kvledger/tests/client.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
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+
"testing"
11+
12+
"github.com/golang/protobuf/proto"
13+
"github.com/hyperledger/fabric/common/util"
14+
"github.com/hyperledger/fabric/core/common/ccprovider"
15+
"github.com/hyperledger/fabric/core/common/privdata"
16+
"github.com/hyperledger/fabric/core/ledger"
17+
"github.com/stretchr/testify/assert"
18+
)
19+
20+
// client helps in a transction simulation. The client keeps accumlating the results of each simulated transaction
21+
// in a slice and at a later stage can be used to cut a test block for committing.
22+
// In a test, for each instantiated ledger, a single instance of a client is typically sufficient.
23+
type client struct {
24+
lgr ledger.PeerLedger
25+
simulatedTrans []*txAndPvtdata // accumulates the results of transactions simulations
26+
assert *assert.Assertions
27+
}
28+
29+
func newClient(lgr ledger.PeerLedger, t *testing.T) *client {
30+
return &client{lgr, nil, assert.New(t)}
31+
}
32+
33+
// simulateDataTx takes a simulation logic and wraps it between
34+
// (A) the pre-simulation tasks (such as obtaining a fresh simulator) and
35+
// (B) the post simulation tasks (such as gathering (public and pvt) simulation results and constructing a transaction)
36+
// Since (A) and (B) both are handled in this function, the test code can be kept simple by just supplying the simulation logic
37+
func (c *client) simulateDataTx(txid string, simulationLogic func(s *simulator)) *txAndPvtdata {
38+
if txid == "" {
39+
txid = util.GenerateUUID()
40+
}
41+
ledgerSimulator, err := c.lgr.NewTxSimulator(txid)
42+
c.assert.NoError(err)
43+
sim := &simulator{ledgerSimulator, txid, c.assert}
44+
simulationLogic(sim)
45+
txAndPvtdata := sim.done()
46+
c.simulatedTrans = append(c.simulatedTrans, txAndPvtdata)
47+
return txAndPvtdata
48+
}
49+
50+
// simulateDeployTx mimics a transction that deploys a chaincode. This in turn calls the function 'simulateDataTx'
51+
// with supplying the simulation logic that mimics the inoke funciton of 'lscc' for the ledger tests
52+
func (c *client) simulateDeployTx(ccName string, collConfs []*collConf) *txAndPvtdata {
53+
ccData := &ccprovider.ChaincodeData{Name: ccName}
54+
ccDataBytes, err := proto.Marshal(ccData)
55+
c.assert.NoError(err)
56+
57+
psudoLSCCInvokeFunc := func(s *simulator) {
58+
s.setState("lscc", ccName, string(ccDataBytes))
59+
if collConfs != nil {
60+
protoBytes, err := convertToCollConfigProtoBytes(collConfs)
61+
c.assert.NoError(err)
62+
s.setState("lscc", privdata.BuildCollectionKVSKey(ccName), string(protoBytes))
63+
}
64+
}
65+
return c.simulateDataTx("", psudoLSCCInvokeFunc)
66+
}
67+
68+
// simulateUpgradeTx see comments on function 'simulateDeployTx'
69+
func (c *client) simulateUpgradeTx(ccName string, collConfs []*collConf) *txAndPvtdata {
70+
return c.simulateDeployTx(ccName, collConfs)
71+
}
72+
73+
/////////////////////// simulator wrapper functions ///////////////////////
74+
type simulator struct {
75+
ledger.TxSimulator
76+
txid string
77+
assert *assert.Assertions
78+
}
79+
80+
func (s *simulator) getState(ns, key string) string {
81+
val, err := s.GetState(ns, key)
82+
s.assert.NoError(err)
83+
return string(val)
84+
}
85+
86+
func (s *simulator) setState(ns, key string, val string) {
87+
s.assert.NoError(
88+
s.SetState(ns, key, []byte(val)),
89+
)
90+
}
91+
92+
func (s *simulator) delState(ns, key string) {
93+
s.assert.NoError(
94+
s.DeleteState(ns, key),
95+
)
96+
}
97+
98+
func (s *simulator) getPvtdata(ns, coll, key string) {
99+
_, err := s.GetPrivateData(ns, coll, key)
100+
s.assert.NoError(err)
101+
}
102+
103+
func (s *simulator) setPvtdata(ns, coll, key string, val string) {
104+
s.assert.NoError(
105+
s.SetPrivateData(ns, coll, key, []byte(val)),
106+
)
107+
}
108+
109+
func (s *simulator) delPvtdata(ns, coll, key string) {
110+
s.assert.NoError(
111+
s.DeletePrivateData(ns, coll, key),
112+
)
113+
}
114+
115+
func (s *simulator) done() *txAndPvtdata {
116+
s.Done()
117+
simRes, err := s.GetTxSimulationResults()
118+
s.assert.NoError(err)
119+
pubRwsetBytes, err := simRes.GetPubSimulationBytes()
120+
s.assert.NoError(err)
121+
envelope, err := constructTransaction(s.txid, pubRwsetBytes)
122+
s.assert.NoError(err)
123+
txAndPvtdata := &txAndPvtdata{Txid: s.txid, Envelope: envelope, Pvtws: simRes.PvtSimulationResults}
124+
return txAndPvtdata
125+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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+
"testing"
11+
12+
"github.com/golang/protobuf/proto"
13+
"github.com/hyperledger/fabric/core/ledger"
14+
"github.com/hyperledger/fabric/protos/common"
15+
"github.com/stretchr/testify/assert"
16+
)
17+
18+
// committer helps in cutting a block and commits the block (with pvt data) to the ledger
19+
type committer struct {
20+
lgr ledger.PeerLedger
21+
blkgen *blkGenerator
22+
assert *assert.Assertions
23+
}
24+
25+
func newCommitter(lgr ledger.PeerLedger, t *testing.T) *committer {
26+
return &committer{lgr, newBlockGenerator(lgr, t), assert.New(t)}
27+
}
28+
29+
// cutBlockAndCommitWithPvtdata cuts the next block from the given 'txAndPvtdata' and commits the block (with pvt data) to the ledger
30+
// This function return a copy of 'ledger.BlockAndPvtData' that was submitted to the ledger to commit.
31+
// A copy is returned instead of the actual one because, ledger makes some changes to the submitted block before commit
32+
// (such as setting the metadata) and the test code would want to have the exact copy of the block that was submitted to
33+
// the ledger
34+
func (c *committer) cutBlockAndCommitWithPvtdata(trans ...*txAndPvtdata) *ledger.BlockAndPvtData {
35+
blk := c.blkgen.nextBlockAndPvtdata(trans...)
36+
blkCopy := c.copyOfBlockAndPvtdata(blk)
37+
c.assert.NoError(
38+
c.lgr.CommitWithPvtData(blk),
39+
)
40+
return blkCopy
41+
}
42+
43+
func (c *committer) cutBlockAndCommitExpectError(trans ...*txAndPvtdata) (*ledger.BlockAndPvtData, error) {
44+
blk := c.blkgen.nextBlockAndPvtdata(trans...)
45+
blkCopy := c.copyOfBlockAndPvtdata(blk)
46+
err := c.lgr.CommitWithPvtData(blk)
47+
c.assert.Error(err)
48+
return blkCopy, err
49+
}
50+
51+
func (c *committer) copyOfBlockAndPvtdata(blk *ledger.BlockAndPvtData) *ledger.BlockAndPvtData {
52+
blkBytes, err := proto.Marshal(blk.Block)
53+
c.assert.NoError(err)
54+
blkCopy := &common.Block{}
55+
c.assert.NoError(proto.Unmarshal(blkBytes, blkCopy))
56+
return &ledger.BlockAndPvtData{Block: blkCopy, BlockPvtData: blk.BlockPvtData}
57+
}
58+
59+
///////////////// block generation code ///////////////////////////////////////////
60+
// blkGenerator helps creating the next block for the ledger
61+
type blkGenerator struct {
62+
lastNum uint64
63+
lastHash []byte
64+
assert *assert.Assertions
65+
}
66+
67+
// newBlockGenerator constructs a 'blkGenerator' and initializes the 'blkGenerator'
68+
// from the last block available in the ledger so that the next block can be populated
69+
// with the correct block number and previous block hash
70+
func newBlockGenerator(lgr ledger.PeerLedger, t *testing.T) *blkGenerator {
71+
assert := assert.New(t)
72+
info, err := lgr.GetBlockchainInfo()
73+
assert.NoError(err)
74+
return &blkGenerator{info.Height - 1, info.PreviousBlockHash, assert}
75+
}
76+
77+
// nextBlockAndPvtdata cuts the next block
78+
func (g *blkGenerator) nextBlockAndPvtdata(trans ...*txAndPvtdata) *ledger.BlockAndPvtData {
79+
block := common.NewBlock(g.lastNum+1, g.lastHash)
80+
blockPvtdata := make(map[uint64]*ledger.TxPvtData)
81+
for i, tran := range trans {
82+
seq := uint64(i)
83+
envelopeBytes, _ := proto.Marshal(tran.Envelope)
84+
block.Data.Data = append(block.Data.Data, envelopeBytes)
85+
if tran.Pvtws != nil {
86+
blockPvtdata[seq] = &ledger.TxPvtData{SeqInBlock: seq, WriteSet: tran.Pvtws}
87+
}
88+
}
89+
block.Header.DataHash = block.Data.Hash()
90+
g.lastNum++
91+
g.lastHash = block.Header.Hash()
92+
setBlockFlagsToValid(block)
93+
return &ledger.BlockAndPvtData{Block: block, BlockPvtData: blockPvtdata}
94+
}

core/ledger/kvledger/tests/env.go

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
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+
"os"
11+
"path/filepath"
12+
"testing"
13+
14+
"github.com/hyperledger/fabric/common/ledger/blkstorage/fsblkstorage"
15+
"github.com/hyperledger/fabric/common/ledger/util"
16+
"github.com/hyperledger/fabric/core/ledger/ledgerconfig"
17+
"github.com/hyperledger/fabric/core/ledger/ledgermgmt"
18+
"github.com/hyperledger/fabric/core/peer"
19+
"github.com/spf13/viper"
20+
"github.com/stretchr/testify/assert"
21+
)
22+
23+
type config map[string]interface{}
24+
type rebuildable uint8
25+
26+
const (
27+
rebuildableStatedb rebuildable = 1
28+
rebuildableBlockIndex rebuildable = 2
29+
rebuildableConfigHistory rebuildable = 4
30+
rebuildableHistoryDB rebuildable = 8
31+
)
32+
33+
var (
34+
defaultConfig = config{
35+
"peer.fileSystemPath": "/tmp/fabric/ledgertests",
36+
"ledger.state.stateDatabase": "goleveldb",
37+
}
38+
)
39+
40+
type env struct {
41+
assert *assert.Assertions
42+
}
43+
44+
func newEnv(conf config, t *testing.T) *env {
45+
setupConfigs(conf)
46+
env := &env{assert.New(t)}
47+
initLedgerMgmt()
48+
return env
49+
}
50+
51+
func (e *env) cleanup() {
52+
closeLedgerMgmt()
53+
e.assert.NoError(os.RemoveAll(getLedgerRootPath()))
54+
}
55+
56+
func (e *env) closeAllLedgersAndDrop(flags rebuildable) {
57+
closeLedgerMgmt()
58+
defer initLedgerMgmt()
59+
60+
if flags&rebuildableBlockIndex == rebuildableBlockIndex {
61+
indexPath := getBlockIndexDBPath()
62+
logger.Infof("Deleting blockstore indexdb path [%s]", indexPath)
63+
e.verifyNonEmptyDirExists(indexPath)
64+
e.assert.NoError(os.RemoveAll(indexPath))
65+
}
66+
67+
if flags&rebuildableStatedb == rebuildableStatedb {
68+
statedbPath := getLevelstateDBPath()
69+
logger.Infof("Deleting statedb path [%s]", statedbPath)
70+
e.verifyNonEmptyDirExists(statedbPath)
71+
e.assert.NoError(os.RemoveAll(statedbPath))
72+
}
73+
74+
if flags&rebuildableConfigHistory == rebuildableConfigHistory {
75+
configHistory := getConfigHistoryDBPath()
76+
logger.Infof("Deleting configHistory db path [%s]", configHistory)
77+
e.verifyNonEmptyDirExists(configHistory)
78+
e.assert.NoError(os.RemoveAll(configHistory))
79+
}
80+
}
81+
82+
func (e *env) verifyRebuilablesExist(flags rebuildable) {
83+
if flags&rebuildableStatedb == rebuildableBlockIndex {
84+
e.verifyNonEmptyDirExists(getBlockIndexDBPath())
85+
}
86+
if flags&rebuildableBlockIndex == rebuildableStatedb {
87+
e.verifyNonEmptyDirExists(getLevelstateDBPath())
88+
}
89+
if flags&rebuildableConfigHistory == rebuildableConfigHistory {
90+
e.verifyNonEmptyDirExists(getConfigHistoryDBPath())
91+
}
92+
}
93+
94+
func (e *env) verifyRebuilableDoesNotExist(flags rebuildable) {
95+
if flags&rebuildableStatedb == rebuildableStatedb {
96+
e.verifyDirDoesNotExist(getLevelstateDBPath())
97+
}
98+
if flags&rebuildableStatedb == rebuildableBlockIndex {
99+
e.verifyDirDoesNotExist(getBlockIndexDBPath())
100+
}
101+
if flags&rebuildableConfigHistory == rebuildableConfigHistory {
102+
e.verifyDirDoesNotExist(getConfigHistoryDBPath())
103+
}
104+
}
105+
106+
func (e *env) verifyNonEmptyDirExists(path string) {
107+
empty, err := util.DirEmpty(path)
108+
e.assert.NoError(err)
109+
e.assert.False(empty)
110+
}
111+
112+
func (e *env) verifyDirDoesNotExist(path string) {
113+
exists, _, err := util.FileExists(path)
114+
e.assert.NoError(err)
115+
e.assert.False(exists)
116+
}
117+
118+
// ########################### ledgermgmt and ledgerconfig related functions wrappers #############################
119+
// In the current code, ledgermgmt and ledgerconfigs are packaged scope APIs and hence so are the following
120+
// wrapper APIs. As a TODO, both the ledgermgmt and ledgerconfig can be refactored as separate objects and then
121+
// the instances of these two would be wrapped inside the `env` struct above.
122+
// #################################################################################################################
123+
func setupConfigs(conf config) {
124+
for c, v := range conf {
125+
viper.Set(c, v)
126+
}
127+
}
128+
129+
func initLedgerMgmt() {
130+
ledgermgmt.InitializeExistingTestEnvWithCustomProcessors(peer.ConfigTxProcessors)
131+
}
132+
133+
func closeLedgerMgmt() {
134+
ledgermgmt.Close()
135+
}
136+
137+
func getLedgerRootPath() string {
138+
return ledgerconfig.GetRootPath()
139+
}
140+
141+
func getLevelstateDBPath() string {
142+
return ledgerconfig.GetStateLevelDBPath()
143+
}
144+
145+
func getBlockIndexDBPath() string {
146+
return filepath.Join(ledgerconfig.GetBlockStorePath(), fsblkstorage.IndexDir)
147+
}
148+
149+
func getConfigHistoryDBPath() string {
150+
return ledgerconfig.GetConfigHistoryPath()
151+
}

0 commit comments

Comments
 (0)