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 }