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 }