github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/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/osdi23p228/fabric/integration/nwo"
    25  	"github.com/osdi23p228/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/osdi23p228/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  	err = json.Unmarshal([]byte(channelInfoStr), &channelInfo)
   344  	Expect(err).NotTo(HaveOccurred())
   345  	return int(channelInfo.Height)
   346  }
   347  
   348  func (nh *networkHelper) queryChaincode(peer *nwo.Peer, command commands.ChaincodeQuery, expectedMessage string, expectSuccess bool) {
   349  	sess, err := nh.PeerUserSession(peer, "User1", command)
   350  	Expect(err).NotTo(HaveOccurred())
   351  	if expectSuccess {
   352  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(0))
   353  		Expect(sess).To(gbytes.Say(expectedMessage))
   354  	} else {
   355  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(1))
   356  		Expect(sess.Err).To(gbytes.Say(expectedMessage))
   357  	}
   358  }
   359  
   360  func (nh *networkHelper) invokeChaincode(peer *nwo.Peer, command commands.ChaincodeInvoke) {
   361  	sess, err := nh.PeerUserSession(peer, "User1", command)
   362  	Expect(err).NotTo(HaveOccurred())
   363  	Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(0))
   364  	Expect(sess.Err).To(gbytes.Say("Chaincode invoke successful."))
   365  }
   366  
   367  func (nh *networkHelper) rollback(peer *nwo.Peer, blockNumber int, expectedErrMessage string, expectSuccess bool) {
   368  	rollbackCmd := commands.NodeRollback{ChannelID: nh.channelID, BlockNumber: blockNumber}
   369  	sess, err := nh.PeerUserSession(peer, "User1", rollbackCmd)
   370  	Expect(err).NotTo(HaveOccurred())
   371  	if expectSuccess {
   372  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(0))
   373  	} else {
   374  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(1))
   375  		Expect(sess.Err).To(gbytes.Say(expectedErrMessage))
   376  	}
   377  }
   378  
   379  func (nh *networkHelper) reset(peer *nwo.Peer, expectedErrMessage string, expectSuccess bool) {
   380  	resetCmd := commands.NodeReset{}
   381  	sess, err := nh.PeerUserSession(peer, "User1", resetCmd)
   382  	Expect(err).NotTo(HaveOccurred())
   383  	if expectSuccess {
   384  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(0))
   385  	} else {
   386  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(1))
   387  		Expect(sess.Err).To(gbytes.Say(expectedErrMessage))
   388  	}
   389  }
   390  
   391  func (nh *networkHelper) pause(peer *nwo.Peer, expectedErrMessage string, expectSuccess bool) {
   392  	pauseCmd := commands.NodePause{ChannelID: nh.channelID}
   393  	sess, err := nh.PeerUserSession(peer, "User1", pauseCmd)
   394  	Expect(err).NotTo(HaveOccurred())
   395  	if expectSuccess {
   396  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(0))
   397  	} else {
   398  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(1))
   399  		Expect(sess.Err).To(gbytes.Say(expectedErrMessage))
   400  	}
   401  }
   402  
   403  func (nh *networkHelper) resume(peer *nwo.Peer, expectedErrMessage string, expectSuccess bool) {
   404  	resumeCmd := commands.NodeResume{ChannelID: nh.channelID}
   405  	sess, err := nh.PeerUserSession(peer, "User1", resumeCmd)
   406  	Expect(err).NotTo(HaveOccurred())
   407  	if expectSuccess {
   408  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(0))
   409  	} else {
   410  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit(1))
   411  		Expect(sess.Err).To(gbytes.Say(expectedErrMessage))
   412  	}
   413  }
   414  
   415  func (nh *networkHelper) waitUntilEndorserEnabled(peer *nwo.Peer) {
   416  	Eventually(func() *gbytes.Buffer {
   417  		sess, err := nh.PeerUserSession(peer, "User1", commands.ChannelInfo{
   418  			ChannelID: nh.channelID,
   419  		})
   420  		Expect(err).NotTo(HaveOccurred())
   421  		Eventually(sess, nh.EventuallyTimeout).Should(gexec.Exit())
   422  		return sess.Buffer()
   423  	}, nh.EventuallyTimeout).Should(gbytes.Say("Blockchain info"))
   424  }
   425  
   426  type testHelper struct {
   427  	*networkHelper
   428  }
   429  
   430  func (th *testHelper) addMarble(chaincodeName, marbleDetails string, peer *nwo.Peer) {
   431  	marbleDetailsBase64 := base64.StdEncoding.EncodeToString([]byte(marbleDetails))
   432  
   433  	command := commands.ChaincodeInvoke{
   434  		ChannelID: th.channelID,
   435  		Orderer:   th.OrdererAddress(th.orderer, nwo.ListenPort),
   436  		Name:      chaincodeName,
   437  		Ctor:      `{"Args":["initMarble"]}`,
   438  		Transient: fmt.Sprintf(`{"marble":"%s"}`, marbleDetailsBase64),
   439  		PeerAddresses: []string{
   440  			th.PeerAddress(peer, nwo.ListenPort),
   441  		},
   442  		WaitForEvent: true,
   443  	}
   444  	th.invokeChaincode(peer, command)
   445  }
   446  
   447  // assertPresentInCollectionM asserts that the private data for given marble is present in collection
   448  // 'readMarble' at the given peers
   449  func (th *testHelper) assertPresentInCollectionM(chaincodeName, marbleName string, peerList ...*nwo.Peer) {
   450  	command := commands.ChaincodeQuery{
   451  		ChannelID: th.channelID,
   452  		Name:      chaincodeName,
   453  		Ctor:      fmt.Sprintf(`{"Args":["readMarble","%s"]}`, marbleName),
   454  	}
   455  	expectedMsg := fmt.Sprintf(`{"docType":"marble","name":"%s"`, marbleName)
   456  	for _, peer := range peerList {
   457  		th.queryChaincode(peer, command, expectedMsg, true)
   458  	}
   459  }
   460  
   461  // assertPresentInCollectionMPD asserts that the private data for given marble is present
   462  // in collection 'readMarblePrivateDetails' at the given peers
   463  func (th *testHelper) assertPresentInCollectionMPD(chaincodeName, marbleName string, peerList ...*nwo.Peer) {
   464  	command := commands.ChaincodeQuery{
   465  		ChannelID: th.channelID,
   466  		Name:      chaincodeName,
   467  		Ctor:      fmt.Sprintf(`{"Args":["readMarblePrivateDetails","%s"]}`, marbleName),
   468  	}
   469  	expectedMsg := fmt.Sprintf(`{"docType":"marblePrivateDetails","name":"%s"`, marbleName)
   470  	for _, peer := range peerList {
   471  		th.queryChaincode(peer, command, expectedMsg, true)
   472  	}
   473  }
   474  
   475  func (th *testHelper) assertDisabledEndorser(chaincodeName string, peer *nwo.Peer) {
   476  	command := commands.ChaincodeQuery{
   477  		ChannelID: th.channelID,
   478  		Name:      chaincodeName,
   479  		Ctor:      `{"Args":["readMarble","marble1"]}`,
   480  	}
   481  	expectedMsg := "endorse requests are blocked while ledgers are being rebuilt"
   482  	th.queryChaincode(peer, command, expectedMsg, false)
   483  }
   484  
   485  func (th *testHelper) assertPausedChannel(peer *nwo.Peer) {
   486  	sess, err := th.PeerUserSession(peer, "User1", commands.ChannelInfo{
   487  		ChannelID: th.channelID,
   488  	})
   489  	Expect(err).NotTo(HaveOccurred())
   490  	Eventually(sess, th.EventuallyTimeout).Should(gexec.Exit(1))
   491  	Expect(sess.Err).To(gbytes.Say("Invalid chain ID"))
   492  }