github.com/anjalikarhana/fabric@v2.1.1+incompatible/integration/ledger/reset_rollback_test.go (about)

     1  /*
     2  Copyright IBM Corp All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package ledger
     8  
     9  import (
    10  	"encoding/base64"
    11  	"encoding/json"
    12  	"fmt"
    13  	"io/ioutil"
    14  	"os"
    15  	"path/filepath"
    16  	"strings"
    17  	"syscall"
    18  
    19  	. "github.com/onsi/ginkgo"
    20  	. "github.com/onsi/gomega"
    21  
    22  	docker "github.com/fsouza/go-dockerclient"
    23  	"github.com/hyperledger/fabric-protos-go/common"
    24  	"github.com/hyperledger/fabric/integration/nwo"
    25  	"github.com/hyperledger/fabric/integration/nwo/commands"
    26  	"github.com/onsi/gomega/gbytes"
    27  	"github.com/onsi/gomega/gexec"
    28  	"github.com/tedsuo/ifrit"
    29  	"gopkg.in/yaml.v2"
    30  )
    31  
    32  var _ = Describe("rollback, reset, pause and resume peer node commands", func() {
    33  	// at the beginning of each test under this block, we have defined two collections:
    34  	// 1. collectionMarbles - Org1 and Org2 have access to this collection
    35  	// 2. collectionMarblePrivateDetails - Org2 and Org3 have access to this collection
    36  	// when calling QueryChaincode with first arg "readMarble", it will query collectionMarbles[1]
    37  	// when calling QueryChaincode with first arg "readMarblePrivateDetails", it will query collectionMarblePrivateDetails[2]
    38  	var (
    39  		setup  *setup
    40  		helper *testHelper
    41  	)
    42  
    43  	BeforeEach(func() {
    44  		setup = initThreeOrgsSetup()
    45  		nwo.EnableCapabilities(setup.network, setup.channelID, "Application", "V2_0", setup.orderer, setup.peers...)
    46  		helper = &testHelper{
    47  			networkHelper: &networkHelper{
    48  				Network:   setup.network,
    49  				orderer:   setup.orderer,
    50  				peers:     setup.peers,
    51  				testDir:   setup.testDir,
    52  				channelID: setup.channelID,
    53  			},
    54  		}
    55  
    56  		By("installing and instantiating chaincode on all peers")
    57  		chaincode := nwo.Chaincode{
    58  			Name:              "marblesp",
    59  			Version:           "1.0",
    60  			Path:              components.Build("github.com/hyperledger/fabric/integration/chaincode/marbles_private/cmd"),
    61  			Lang:              "binary",
    62  			PackageFile:       filepath.Join(setup.testDir, "marbles-pvtdata.tar.gz"),
    63  			Label:             "marbles-private-20",
    64  			SignaturePolicy:   `OR ('Org1MSP.member','Org2MSP.member', 'Org3MSP.member')`,
    65  			CollectionsConfig: filepath.Join("testdata", "collection_configs", "collections_config1.json"),
    66  			Sequence:          "1",
    67  		}
    68  
    69  		helper.deployChaincode(chaincode)
    70  
    71  		org2peer0 := setup.network.Peer("org2", "peer0")
    72  		height := helper.getLedgerHeight(org2peer0)
    73  
    74  		By("creating 5 blocks")
    75  		for i := 1; i <= 5; i++ {
    76  			helper.addMarble("marblesp", fmt.Sprintf(`{"name":"marble%d", "color":"blue", "size":35, "owner":"tom", "price":99}`, i), org2peer0)
    77  			helper.waitUntilEqualLedgerHeight(height + i)
    78  		}
    79  
    80  		By("verifying marble1 to marble5 exist in collectionMarbles & collectionMarblePrivateDetails in peer0.org2")
    81  		for i := 1; i <= 5; i++ {
    82  			helper.assertPresentInCollectionM("marblesp", fmt.Sprintf("marble%d", i), org2peer0)
    83  			helper.assertPresentInCollectionMPD("marblesp", fmt.Sprintf("marble%d", i), org2peer0)
    84  		}
    85  	})
    86  
    87  	AfterEach(func() {
    88  		setup.cleanup()
    89  	})
    90  
    91  	// This test executes the rollback, reset, pause, and resume commands on the following peerss
    92  	// org1.peer0 - rollback
    93  	// org2.peer0 - reset
    94  	// org3.peer0 - pause/rollback/resume
    95  	//
    96  	// There are 14 blocks created in BeforeEach (before rollback/reset).
    97  	// block 0: genesis, block 1: org1Anchor, block 2: org2Anchor, block 3: org3Anchor
    98  	// block 4 to 8: chaincode instantiation, block 9 to 13: chaincode invoke to add marbles.
    99  	It("pauses and resumes channels and rolls back and resets the ledger", func() {
   100  		By("Checking ledger height on each peer")
   101  		for _, peer := range helper.peers {
   102  			Expect(helper.getLedgerHeight(peer)).Should(Equal(14))
   103  		}
   104  
   105  		org1peer0 := setup.network.Peer("org1", "peer0")
   106  		org2peer0 := setup.network.Peer("org2", "peer0")
   107  		org3peer0 := setup.network.Peer("org3", "peer0")
   108  
   109  		// Negative test: rollback, reset, pause, and resume should fail when the peer is online
   110  		expectedErrMessage := "as another peer node command is executing," +
   111  			" wait for that command to complete its execution or terminate it before retrying"
   112  		By("Rolling back the peer to block 6 from block 13 while the peer node is online")
   113  		helper.rollback(org1peer0, 6, expectedErrMessage, false)
   114  		By("Resetting the peer to the genesis block while the peer node is online")
   115  		helper.reset(org2peer0, expectedErrMessage, false)
   116  		By("Pausing the peer while the peer node is online")
   117  		helper.pause(org3peer0, expectedErrMessage, false)
   118  		By("Resuming the peer while the peer node is online")
   119  		helper.resume(org3peer0, expectedErrMessage, false)
   120  
   121  		By("Stopping the network to test commands")
   122  		setup.terminateAllProcess()
   123  
   124  		By("Rolling back the channel to block 6 from block 14 on org1peer0")
   125  		helper.rollback(org1peer0, 6, "", true)
   126  
   127  		By("Resetting org2peer0 to the genesis block")
   128  		helper.reset(org2peer0, "", true)
   129  
   130  		By("Pausing the channel on org3peer0")
   131  		helper.pause(org3peer0, "", true)
   132  
   133  		By("Rolling back the paused channel to block 6 from block 14 on org3peer0")
   134  		helper.rollback(org3peer0, 6, "", true)
   135  
   136  		By("Verifying paused channel is not found upon peer restart")
   137  		setup.startPeer(org3peer0)
   138  		helper.assertPausedChannel(org3peer0)
   139  
   140  		By("Checking preResetHeightFile exists for a paused channel that is also rolled back or reset")
   141  		setup.startBrokerAndOrderer()
   142  		preResetHeightFile := filepath.Join(setup.network.PeerLedgerDir(org3peer0), "chains/chains", helper.channelID, "__preResetHeight")
   143  		Expect(preResetHeightFile).To(BeARegularFile())
   144  
   145  		setup.terminateAllProcess()
   146  
   147  		By("Resuming the peer")
   148  		helper.resume(org3peer0, "", true)
   149  
   150  		By("Verifying that the endorsement is disabled when the peer has not received missing blocks")
   151  		setup.startPeers()
   152  		for _, peer := range setup.peers {
   153  			helper.assertDisabledEndorser("marblesp", peer)
   154  		}
   155  
   156  		By("Bringing the peers to recent height by starting the orderer")
   157  		setup.startBrokerAndOrderer()
   158  		for _, peer := range setup.peers {
   159  			By("Verifying endorsement is enabled and preResetHeightFile is removed on peer " + peer.ID())
   160  			helper.waitUntilEndorserEnabled(peer)
   161  			preResetHeightFile := filepath.Join(setup.network.PeerLedgerDir(peer), "chains/chains", helper.channelID, "__preResetHeight")
   162  			Expect(preResetHeightFile).NotTo(BeAnExistingFile())
   163  		}
   164  
   165  		setup.network.VerifyMembership(setup.peers, setup.channelID, "marblesp")
   166  
   167  		By("Verifying leger height on all peers")
   168  		helper.waitUntilEqualLedgerHeight(14)
   169  
   170  		// Test chaincode works correctly after the commands
   171  		By("Creating 2 more blocks post rollback/reset")
   172  		for i := 6; i <= 7; i++ {
   173  			helper.addMarble("marblesp", fmt.Sprintf(`{"name":"marble%d", "color":"blue", "size":35, "owner":"tom", "price":99}`, i), org2peer0)
   174  			helper.waitUntilEqualLedgerHeight(14 + i - 5)
   175  		}
   176  
   177  		By("Verifying marble1 to marble7 exist in collectionMarbles & collectionMarblePrivateDetails on org2peer0")
   178  		for i := 1; i <= 7; i++ {
   179  			helper.assertPresentInCollectionM("marblesp", fmt.Sprintf("marble%d", i), org2peer0)
   180  			helper.assertPresentInCollectionMPD("marblesp", fmt.Sprintf("marble%d", i), org2peer0)
   181  		}
   182  
   183  		// statedb rebuild test
   184  		By("Stopping peers and deleting the statedb folder on peer org2.peer0")
   185  		peer := setup.network.Peer("org2", "peer0")
   186  		setup.stopPeers()
   187  		dbPath := filepath.Join(setup.network.PeerLedgerDir(peer), "stateLeveldb")
   188  		Expect(os.RemoveAll(dbPath)).NotTo(HaveOccurred())
   189  		Expect(dbPath).NotTo(BeADirectory())
   190  		By("Restarting the peer org2.peer0")
   191  		setup.startPeer(peer)
   192  		Expect(dbPath).To(BeADirectory())
   193  		helper.assertPresentInCollectionM("marblesp", "marble2", peer)
   194  	})
   195  })
   196  
   197  type setup struct {
   198  	testDir        string
   199  	channelID      string
   200  	network        *nwo.Network
   201  	peers          []*nwo.Peer
   202  	peerProcess    []ifrit.Process
   203  	orderer        *nwo.Orderer
   204  	ordererProcess ifrit.Process
   205  	brokerProcess  ifrit.Process
   206  }
   207  
   208  func initThreeOrgsSetup() *setup {
   209  	var err error
   210  	testDir, err := ioutil.TempDir("", "reset-rollback")
   211  	Expect(err).NotTo(HaveOccurred())
   212  
   213  	client, err := docker.NewClientFromEnv()
   214  	Expect(err).NotTo(HaveOccurred())
   215  
   216  	configBytes, err := ioutil.ReadFile(filepath.Join("testdata", "network.yaml"))
   217  	Expect(err).NotTo(HaveOccurred())
   218  
   219  	var networkConfig *nwo.Config
   220  	err = yaml.Unmarshal(configBytes, &networkConfig)
   221  	Expect(err).NotTo(HaveOccurred())
   222  
   223  	n := nwo.New(networkConfig, testDir, client, StartPort(), components)
   224  	n.GenerateConfigTree()
   225  	n.Bootstrap()
   226  
   227  	peers := []*nwo.Peer{
   228  		n.Peer("org1", "peer0"),
   229  		n.Peer("org2", "peer0"),
   230  		n.Peer("org3", "peer0"),
   231  	}
   232  
   233  	setup := &setup{
   234  		testDir:   testDir,
   235  		network:   n,
   236  		peers:     peers,
   237  		channelID: "testchannel",
   238  	}
   239  
   240  	setup.startBrokerAndOrderer()
   241  
   242  	setup.startPeer(peers[0])
   243  	setup.startPeer(peers[1])
   244  	setup.startPeer(peers[2])
   245  
   246  	orderer := n.Orderer("orderer")
   247  	n.CreateAndJoinChannel(orderer, "testchannel")
   248  	n.UpdateChannelAnchors(orderer, "testchannel")
   249  	setup.orderer = orderer
   250  
   251  	By("verifying membership")
   252  	setup.network.VerifyMembership(setup.peers, setup.channelID)
   253  
   254  	return setup
   255  }
   256  
   257  func (s *setup) cleanup() {
   258  	s.terminateAllProcess()
   259  	s.network.Cleanup()
   260  	os.RemoveAll(s.testDir)
   261  }
   262  
   263  func (s *setup) terminateAllProcess() {
   264  	s.ordererProcess.Signal(syscall.SIGTERM)
   265  	Eventually(s.ordererProcess.Wait(), s.network.EventuallyTimeout).Should(Receive())
   266  	s.ordererProcess = nil
   267  
   268  	s.brokerProcess.Signal(syscall.SIGTERM)
   269  	Eventually(s.brokerProcess.Wait(), s.network.EventuallyTimeout).Should(Receive())
   270  	s.brokerProcess = nil
   271  
   272  	for _, p := range s.peerProcess {
   273  		p.Signal(syscall.SIGTERM)
   274  		Eventually(p.Wait(), s.network.EventuallyTimeout).Should(Receive())
   275  	}
   276  	s.peerProcess = nil
   277  }
   278  
   279  func (s *setup) startPeers() {
   280  	for _, peer := range s.peers {
   281  		s.startPeer(peer)
   282  	}
   283  }
   284  
   285  func (s *setup) stopPeers() {
   286  	for _, p := range s.peerProcess {
   287  		p.Signal(syscall.SIGTERM)
   288  		Eventually(p.Wait(), s.network.EventuallyTimeout).Should(Receive())
   289  	}
   290  	s.peerProcess = nil
   291  }
   292  
   293  func (s *setup) startPeer(peer *nwo.Peer) {
   294  	peerRunner := s.network.PeerRunner(peer)
   295  	peerProcess := ifrit.Invoke(peerRunner)
   296  	Eventually(peerProcess.Ready(), s.network.EventuallyTimeout).Should(BeClosed())
   297  	s.peerProcess = append(s.peerProcess, peerProcess)
   298  }
   299  
   300  func (s *setup) startBrokerAndOrderer() {
   301  
   302  	brokerRunner := s.network.BrokerGroupRunner()
   303  	brokerProcess := ifrit.Invoke(brokerRunner)
   304  	Eventually(brokerProcess.Ready(), s.network.EventuallyTimeout).Should(BeClosed())
   305  	s.brokerProcess = brokerProcess
   306  
   307  	ordererRunner := s.network.OrdererGroupRunner()
   308  	ordererProcess := ifrit.Invoke(ordererRunner)
   309  	Eventually(ordererProcess.Ready(), s.network.EventuallyTimeout).Should(BeClosed())
   310  	s.ordererProcess = ordererProcess
   311  }
   312  
   313  type networkHelper struct {
   314  	*nwo.Network
   315  	orderer   *nwo.Orderer
   316  	peers     []*nwo.Peer
   317  	channelID string
   318  	testDir   string
   319  }
   320  
   321  func (nh *networkHelper) deployChaincode(chaincode nwo.Chaincode) {
   322  	nwo.DeployChaincode(nh.Network, nh.channelID, nh.orderer, chaincode)
   323  	nh.waitUntilEqualLedgerHeight(nh.getLedgerHeight(nh.peers[0]))
   324  }
   325  
   326  func (nh *networkHelper) waitUntilEqualLedgerHeight(height int) {
   327  	for _, peer := range nh.peers {
   328  		Eventually(func() int {
   329  			return nh.getLedgerHeight(peer)
   330  		}, nh.EventuallyTimeout).Should(Equal(height))
   331  	}
   332  }
   333  
   334  func (nh *networkHelper) getLedgerHeight(peer *nwo.Peer) int {
   335  	sess, err := nh.PeerUserSession(peer, "User1", commands.ChannelInfo{
   336  		ChannelID: nh.channelID,
   337  	})
   338  	Expect(err).NotTo(HaveOccurred())
   339  	Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(0))
   340  
   341  	channelInfoStr := strings.TrimPrefix(string(sess.Buffer().Contents()[:]), "Blockchain info:")
   342  	var channelInfo = common.BlockchainInfo{}
   343  	json.Unmarshal([]byte(channelInfoStr), &channelInfo)
   344  	return int(channelInfo.Height)
   345  }
   346  
   347  func (nh *networkHelper) queryChaincode(peer *nwo.Peer, command commands.ChaincodeQuery, expectedMessage string, expectSuccess bool) {
   348  	sess, err := nh.PeerUserSession(peer, "User1", command)
   349  	Expect(err).NotTo(HaveOccurred())
   350  	if expectSuccess {
   351  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(0))
   352  		Expect(sess).To(gbytes.Say(expectedMessage))
   353  	} else {
   354  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(1))
   355  		Expect(sess.Err).To(gbytes.Say(expectedMessage))
   356  	}
   357  }
   358  
   359  func (nh *networkHelper) invokeChaincode(peer *nwo.Peer, command commands.ChaincodeInvoke) {
   360  	sess, err := nh.PeerUserSession(peer, "User1", command)
   361  	Expect(err).NotTo(HaveOccurred())
   362  	Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(0))
   363  	Expect(sess.Err).To(gbytes.Say("Chaincode invoke successful."))
   364  }
   365  
   366  func (nh *networkHelper) rollback(peer *nwo.Peer, blockNumber int, expectedErrMessage string, expectSuccess bool) {
   367  	rollbackCmd := commands.NodeRollback{ChannelID: nh.channelID, BlockNumber: blockNumber}
   368  	sess, err := nh.PeerUserSession(peer, "User1", rollbackCmd)
   369  	Expect(err).NotTo(HaveOccurred())
   370  	if expectSuccess {
   371  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(0))
   372  	} else {
   373  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(1))
   374  		Expect(sess.Err).To(gbytes.Say(expectedErrMessage))
   375  	}
   376  }
   377  
   378  func (nh *networkHelper) reset(peer *nwo.Peer, expectedErrMessage string, expectSuccess bool) {
   379  	resetCmd := commands.NodeReset{}
   380  	sess, err := nh.PeerUserSession(peer, "User1", resetCmd)
   381  	Expect(err).NotTo(HaveOccurred())
   382  	if expectSuccess {
   383  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(0))
   384  	} else {
   385  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(1))
   386  		Expect(sess.Err).To(gbytes.Say(expectedErrMessage))
   387  	}
   388  }
   389  
   390  func (nh *networkHelper) pause(peer *nwo.Peer, expectedErrMessage string, expectSuccess bool) {
   391  	pauseCmd := commands.NodePause{ChannelID: nh.channelID}
   392  	sess, err := nh.PeerUserSession(peer, "User1", pauseCmd)
   393  	Expect(err).NotTo(HaveOccurred())
   394  	if expectSuccess {
   395  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(0))
   396  	} else {
   397  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(1))
   398  		Expect(sess.Err).To(gbytes.Say(expectedErrMessage))
   399  	}
   400  }
   401  
   402  func (nh *networkHelper) resume(peer *nwo.Peer, expectedErrMessage string, expectSuccess bool) {
   403  	resumeCmd := commands.NodeResume{ChannelID: nh.channelID}
   404  	sess, err := nh.PeerUserSession(peer, "User1", resumeCmd)
   405  	Expect(err).NotTo(HaveOccurred())
   406  	if expectSuccess {
   407  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(0))
   408  	} else {
   409  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(1))
   410  		Expect(sess.Err).To(gbytes.Say(expectedErrMessage))
   411  	}
   412  }
   413  
   414  func (nh *networkHelper) waitUntilEndorserEnabled(peer *nwo.Peer) {
   415  	Eventually(func() *gbytes.Buffer {
   416  		sess, err := nh.PeerUserSession(peer, "User1", commands.ChannelInfo{
   417  			ChannelID: nh.channelID,
   418  		})
   419  		Expect(err).NotTo(HaveOccurred())
   420  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit())
   421  		return sess.Buffer()
   422  	}, nh.EventuallyTimeout).Should(gbytes.Say("Blockchain info"))
   423  }
   424  
   425  type testHelper struct {
   426  	*networkHelper
   427  }
   428  
   429  func (th *testHelper) addMarble(chaincodeName, marbleDetails string, peer *nwo.Peer) {
   430  	marbleDetailsBase64 := base64.StdEncoding.EncodeToString([]byte(marbleDetails))
   431  
   432  	command := commands.ChaincodeInvoke{
   433  		ChannelID: th.channelID,
   434  		Orderer:   th.OrdererAddress(th.orderer, nwo.ListenPort),
   435  		Name:      chaincodeName,
   436  		Ctor:      fmt.Sprintf(`{"Args":["initMarble"]}`),
   437  		Transient: fmt.Sprintf(`{"marble":"%s"}`, marbleDetailsBase64),
   438  		PeerAddresses: []string{
   439  			th.PeerAddress(peer, nwo.ListenPort),
   440  		},
   441  		WaitForEvent: true,
   442  	}
   443  	th.invokeChaincode(peer, command)
   444  }
   445  
   446  // assertPresentInCollectionM asserts that the private data for given marble is present in collection
   447  // 'readMarble' at the given peers
   448  func (th *testHelper) assertPresentInCollectionM(chaincodeName, marbleName string, peerList ...*nwo.Peer) {
   449  	command := commands.ChaincodeQuery{
   450  		ChannelID: th.channelID,
   451  		Name:      chaincodeName,
   452  		Ctor:      fmt.Sprintf(`{"Args":["readMarble","%s"]}`, marbleName),
   453  	}
   454  	expectedMsg := fmt.Sprintf(`{"docType":"marble","name":"%s"`, marbleName)
   455  	for _, peer := range peerList {
   456  		th.queryChaincode(peer, command, expectedMsg, true)
   457  	}
   458  }
   459  
   460  // assertPresentInCollectionMPD asserts that the private data for given marble is present
   461  // in collection 'readMarblePrivateDetails' at the given peers
   462  func (th *testHelper) assertPresentInCollectionMPD(chaincodeName, marbleName string, peerList ...*nwo.Peer) {
   463  	command := commands.ChaincodeQuery{
   464  		ChannelID: th.channelID,
   465  		Name:      chaincodeName,
   466  		Ctor:      fmt.Sprintf(`{"Args":["readMarblePrivateDetails","%s"]}`, marbleName),
   467  	}
   468  	expectedMsg := fmt.Sprintf(`{"docType":"marblePrivateDetails","name":"%s"`, marbleName)
   469  	for _, peer := range peerList {
   470  		th.queryChaincode(peer, command, expectedMsg, true)
   471  	}
   472  }
   473  
   474  func (th *testHelper) assertDisabledEndorser(chaincodeName string, peer *nwo.Peer) {
   475  	command := commands.ChaincodeQuery{
   476  		ChannelID: th.channelID,
   477  		Name:      chaincodeName,
   478  		Ctor:      `{"Args":["readMarble","marble1"]}`,
   479  	}
   480  	expectedMsg := "endorse requests are blocked while ledgers are being rebuilt"
   481  	th.queryChaincode(peer, command, expectedMsg, false)
   482  }
   483  
   484  func (th *testHelper) assertPausedChannel(peer *nwo.Peer) {
   485  	sess, err := th.PeerUserSession(peer, "User1", commands.ChannelInfo{
   486  		ChannelID: th.channelID,
   487  	})
   488  	Expect(err).NotTo(HaveOccurred())
   489  	Eventually(sess, th.EventuallyTimeout).Should(gexec.Exit(1))
   490  	Expect(sess.Err).To(gbytes.Say("Invalid chain ID"))
   491  }