github.com/klaytn/klaytn@v1.10.2/node/sc/vt_recovery_test.go (about) 1 // Copyright 2019 The klaytn Authors 2 // This file is part of the klaytn library. 3 // 4 // The klaytn library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The klaytn library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>. 16 17 package sc 18 19 import ( 20 "io/ioutil" 21 "log" 22 "math/big" 23 "os" 24 "sync" 25 "testing" 26 "time" 27 28 "github.com/klaytn/klaytn/accounts/abi/bind" 29 "github.com/klaytn/klaytn/accounts/abi/bind/backends" 30 "github.com/klaytn/klaytn/blockchain" 31 "github.com/klaytn/klaytn/blockchain/types" 32 "github.com/klaytn/klaytn/common" 33 sctoken "github.com/klaytn/klaytn/contracts/sc_erc20" 34 scnft "github.com/klaytn/klaytn/contracts/sc_erc721" 35 "github.com/klaytn/klaytn/crypto" 36 "github.com/klaytn/klaytn/params" 37 "github.com/klaytn/klaytn/storage/database" 38 "github.com/stretchr/testify/assert" 39 ) 40 41 type testInfo struct { 42 t *testing.T 43 sim *backends.SimulatedBackend 44 sc *SubBridge 45 bm *BridgeManager 46 localInfo *BridgeInfo 47 remoteInfo *BridgeInfo 48 tokenLocalAddr common.Address 49 tokenRemoteAddr common.Address 50 tokenLocalBridge *sctoken.ServiceChainToken 51 tokenRemoteBridge *sctoken.ServiceChainToken 52 nftLocalAddr common.Address 53 nftRemoteAddr common.Address 54 nftLocalBridge *scnft.ServiceChainNFT 55 nftRemoteBridge *scnft.ServiceChainNFT 56 nodeAuth *bind.TransactOpts 57 chainAuth *bind.TransactOpts 58 aliceAuth *bind.TransactOpts 59 recoveryCh chan bool 60 mu sync.Mutex 61 nftIndex int64 62 } 63 64 const ( 65 testGasLimit = 1000000 66 testAmount = 321 67 testToken = 123 68 testNFT = int64(7321) 69 testChargeToken = 100000 70 testTimeout = 10 * time.Second 71 testTxCount = 7 72 testBlockOffset = 3 // +2 for genesis and bridge contract, +1 by a hardcoded hint 73 testPendingCount = 3 74 ) 75 76 type operations struct { 77 request func(*testInfo, *BridgeInfo) 78 handle func(*testInfo, *BridgeInfo, IRequestValueTransferEvent) 79 dummyHandle func(*testInfo, *BridgeInfo) 80 } 81 82 var ops = map[uint8]*operations{ 83 KLAY: { 84 request: requestKLAYTransfer, 85 handle: handleKLAYTransfer, 86 dummyHandle: dummyHandleRequestKLAYTransfer, 87 }, 88 ERC20: { 89 request: requestTokenTransfer, 90 handle: handleTokenTransfer, 91 dummyHandle: dummyHandleRequestTokenTransfer, 92 }, 93 ERC721: { 94 request: requestNFTTransfer, 95 handle: handleNFTTransfer, 96 dummyHandle: dummyHandleRequestNFTTransfer, 97 }, 98 } 99 100 // TestBasicKLAYTransferRecovery tests each methods of the value transfer recovery. 101 func TestBasicKLAYTransferRecovery(t *testing.T) { 102 tempDir, err := ioutil.TempDir(os.TempDir(), "sc") 103 assert.NoError(t, err) 104 defer func() { 105 if err := os.RemoveAll(tempDir); err != nil { 106 t.Fatalf("fail to delete file %v", err) 107 } 108 }() 109 110 // 1. Init dummy chain and do some value transfers. 111 info := prepare(t, func(info *testInfo) { 112 for i := 0; i < testTxCount; i++ { 113 ops[KLAY].request(info, info.localInfo) 114 } 115 }) 116 defer info.sim.Close() 117 vtr := NewValueTransferRecovery(&SCConfig{VTRecovery: true}, info.localInfo, info.remoteInfo) 118 119 // 2. Update recovery hint. 120 err = vtr.updateRecoveryHint() 121 if err != nil { 122 t.Fatal("fail to update value transfer hint") 123 } 124 t.Log("value transfer hint", vtr.child2parentHint) 125 assert.Equal(t, uint64(testTxCount), vtr.child2parentHint.requestNonce) 126 assert.Equal(t, uint64(testTxCount-testPendingCount), vtr.child2parentHint.handleNonce) 127 128 // 3. Request events by using the hint. 129 err = vtr.retrievePendingEvents() 130 if err != nil { 131 t.Fatal("fail to retrieve pending events from the bridge contract") 132 } 133 134 // 4. Check pending events. 135 t.Log("check pending tx", "len", len(vtr.childEvents)) 136 count := 0 137 for _, ev := range vtr.childEvents { 138 assert.Equal(t, info.nodeAuth.From, ev.GetFrom()) 139 assert.Equal(t, info.aliceAuth.From, ev.GetTo()) 140 assert.Equal(t, big.NewInt(testAmount), ev.GetValueOrTokenId()) 141 assert.Condition(t, func() bool { 142 return uint64(testBlockOffset) <= ev.GetRaw().BlockNumber 143 }) 144 count++ 145 } 146 assert.Equal(t, testPendingCount, count) 147 148 // 5. Recover pending events 149 info.recoveryCh <- true 150 assert.Equal(t, nil, vtr.recoverPendingEvents()) 151 ops[KLAY].dummyHandle(info, info.remoteInfo) 152 153 // 6. Check empty pending events. 154 err = vtr.updateRecoveryHint() 155 if err != nil { 156 t.Fatal("fail to update value transfer hint") 157 } 158 err = vtr.retrievePendingEvents() 159 if err != nil { 160 t.Fatal("fail to retrieve pending events from the bridge contract") 161 } 162 assert.Equal(t, 0, len(vtr.childEvents)) 163 164 assert.Equal(t, nil, vtr.Recover()) // nothing to recover 165 } 166 167 // TestKLAYTransferLongRangeRecovery tests a long block range recovery. 168 func TestKLAYTransferLongRangeRecovery(t *testing.T) { 169 tempDir := os.TempDir() + "sc" 170 os.MkdirAll(tempDir, os.ModePerm) 171 oldMaxPendingTxs := maxPendingTxs 172 maxPendingTxs = 2 173 defer func() { 174 maxPendingTxs = oldMaxPendingTxs 175 if err := os.RemoveAll(tempDir); err != nil { 176 t.Fatalf("fail to delete file %v", err) 177 } 178 }() 179 180 // 1. Init dummy chain and do some value transfers. 181 info := prepare(t, func(info *testInfo) { 182 for i := 0; i < testTxCount; i++ { 183 ops[KLAY].request(info, info.localInfo) 184 for i := uint64(0); i < filterLogsStride; i++ { 185 info.sim.Commit() 186 } 187 } 188 }) 189 defer info.sim.Close() 190 // TODO-Klaytn need to remove sleep 191 time.Sleep(1 * time.Second) 192 info.sim.Commit() 193 194 vtr := NewValueTransferRecovery(&SCConfig{VTRecovery: true}, info.localInfo, info.remoteInfo) 195 196 err := vtr.updateRecoveryHint() 197 if err != nil { 198 t.Fatal("fail to update a value transfer hint") 199 } 200 assert.Equal(t, uint64(testTxCount), vtr.child2parentHint.requestNonce) 201 assert.Equal(t, uint64(testTxCount-testPendingCount), vtr.child2parentHint.handleNonce) 202 203 // 2. first recovery. 204 info.recoveryCh <- true 205 err = vtr.Recover() 206 if err != nil { 207 t.Fatal("fail to recover the value transfer") 208 } 209 // TODO-Klaytn need to remove sleep 210 time.Sleep(1 * time.Second) 211 info.sim.Commit() 212 213 err = vtr.updateRecoveryHint() 214 if err != nil { 215 t.Fatal("fail to update value transfer hint") 216 } 217 assert.Equal(t, uint64(testTxCount), vtr.child2parentHint.requestNonce) 218 assert.Equal(t, uint64(testTxCount-testPendingCount+maxPendingTxs), vtr.child2parentHint.handleNonce) 219 220 // 3. second recovery. 221 err = vtr.Recover() 222 if err != nil { 223 t.Fatal("fail to recover the value transfer") 224 } 225 // TODO-Klaytn need to remove sleep 226 time.Sleep(1 * time.Second) 227 info.sim.Commit() 228 229 // 4. Check if recovery is done. 230 err = vtr.updateRecoveryHint() 231 if err != nil { 232 t.Fatal("fail to update value transfer hint") 233 } 234 assert.Equal(t, uint64(testTxCount), vtr.child2parentHint.requestNonce) 235 assert.Equal(t, uint64(testTxCount), vtr.child2parentHint.handleNonce) 236 } 237 238 // TestBasicTokenTransferRecovery tests the token transfer recovery. 239 func TestBasicTokenTransferRecovery(t *testing.T) { 240 tempDir, err := ioutil.TempDir(os.TempDir(), "sc") 241 assert.NoError(t, err) 242 defer func() { 243 if err := os.RemoveAll(tempDir); err != nil { 244 t.Fatalf("fail to delete file %v", err) 245 } 246 }() 247 248 info := prepare(t, func(info *testInfo) { 249 for i := 0; i < testTxCount; i++ { 250 ops[ERC20].request(info, info.localInfo) 251 } 252 }) 253 defer info.sim.Close() 254 255 vtr := NewValueTransferRecovery(&SCConfig{VTRecovery: true}, info.localInfo, info.remoteInfo) 256 err = vtr.updateRecoveryHint() 257 if err != nil { 258 t.Fatal("fail to update a value transfer hint") 259 } 260 assert.NotEqual(t, vtr.child2parentHint.requestNonce, vtr.child2parentHint.handleNonce) 261 t.Log("token transfer hint", vtr.child2parentHint) 262 263 info.recoveryCh <- true 264 err = vtr.Recover() 265 if err != nil { 266 t.Fatal("fail to recover the value transfer") 267 } 268 ops[ERC20].dummyHandle(info, info.remoteInfo) 269 270 err = vtr.updateRecoveryHint() 271 if err != nil { 272 t.Fatal("fail to update a value transfer hint") 273 } 274 assert.Equal(t, vtr.child2parentHint.requestNonce, vtr.child2parentHint.handleNonce) 275 } 276 277 // TestBasicNFTTransferRecovery tests the NFT transfer recovery. 278 // TODO-Klaytn-ServiceChain: implement NFT transfer. 279 func TestBasicNFTTransferRecovery(t *testing.T) { 280 tempDir, err := ioutil.TempDir(os.TempDir(), "sc") 281 assert.NoError(t, err) 282 defer func() { 283 if err := os.RemoveAll(tempDir); err != nil { 284 t.Fatalf("fail to delete file %v", err) 285 } 286 }() 287 288 info := prepare(t, func(info *testInfo) { 289 for i := 0; i < testTxCount; i++ { 290 ops[ERC721].request(info, info.localInfo) 291 } 292 }) 293 defer info.sim.Close() 294 295 vtr := NewValueTransferRecovery(&SCConfig{VTRecovery: true}, info.localInfo, info.remoteInfo) 296 err = vtr.updateRecoveryHint() 297 if err != nil { 298 t.Fatal("fail to update a value transfer hint") 299 } 300 assert.NotEqual(t, vtr.child2parentHint.requestNonce, vtr.child2parentHint.handleNonce) 301 t.Log("token transfer hint", vtr.child2parentHint) 302 303 info.recoveryCh <- true 304 err = vtr.Recover() 305 if err != nil { 306 t.Fatal("fail to recover the value transfer") 307 } 308 ops[ERC721].dummyHandle(info, info.remoteInfo) 309 310 err = vtr.updateRecoveryHint() 311 if err != nil { 312 t.Fatal("fail to update a value transfer hint") 313 } 314 assert.Equal(t, vtr.child2parentHint.requestNonce, vtr.child2parentHint.handleNonce) 315 } 316 317 // TestMethodRecover tests the valueTransferRecovery.Recover() method. 318 func TestMethodRecover(t *testing.T) { 319 tempDir, err := ioutil.TempDir(os.TempDir(), "sc") 320 assert.NoError(t, err) 321 defer func() { 322 if err := os.RemoveAll(tempDir); err != nil { 323 t.Fatalf("fail to delete file %v", err) 324 } 325 }() 326 327 info := prepare(t, func(info *testInfo) { 328 for i := 0; i < testTxCount; i++ { 329 ops[KLAY].request(info, info.localInfo) 330 } 331 }) 332 defer info.sim.Close() 333 334 vtr := NewValueTransferRecovery(&SCConfig{VTRecovery: true}, info.localInfo, info.remoteInfo) 335 err = vtr.updateRecoveryHint() 336 if err != nil { 337 t.Fatal("fail to update a value transfer hint") 338 } 339 assert.NotEqual(t, vtr.child2parentHint.requestNonce, vtr.child2parentHint.handleNonce) 340 341 info.recoveryCh <- true 342 err = vtr.Recover() 343 if err != nil { 344 t.Fatal("fail to recover the value transfer") 345 } 346 ops[KLAY].dummyHandle(info, info.remoteInfo) 347 348 err = vtr.updateRecoveryHint() 349 if err != nil { 350 t.Fatal("fail to update a value transfer hint") 351 } 352 assert.Equal(t, vtr.child2parentHint.requestNonce, vtr.child2parentHint.handleNonce) 353 } 354 355 // TestMethodStop tests the Stop method for stop the internal goroutine. 356 func TestMethodStop(t *testing.T) { 357 tempDir, err := ioutil.TempDir(os.TempDir(), "sc") 358 assert.NoError(t, err) 359 defer func() { 360 if err := os.RemoveAll(tempDir); err != nil { 361 t.Fatalf("fail to delete file %v", err) 362 } 363 }() 364 365 info := prepare(t, func(info *testInfo) { 366 for i := 0; i < testTxCount; i++ { 367 ops[KLAY].request(info, info.localInfo) 368 } 369 }) 370 defer info.sim.Close() 371 372 vtr := NewValueTransferRecovery(&SCConfig{VTRecovery: true, VTRecoveryInterval: 1}, info.localInfo, info.remoteInfo) 373 err = vtr.updateRecoveryHint() 374 if err != nil { 375 t.Fatal("fail to update a value transfer hint") 376 } 377 assert.NotEqual(t, vtr.child2parentHint.requestNonce, vtr.child2parentHint.handleNonce) 378 379 info.recoveryCh <- true 380 err = vtr.Start() 381 if err != nil { 382 t.Fatal("fail to start the value transfer") 383 } 384 assert.Equal(t, nil, vtr.WaitRunningStatus(true, 5*time.Second)) 385 err = vtr.Stop() 386 if err != nil { 387 t.Fatal("fail to stop the value transfer") 388 } 389 assert.Equal(t, false, vtr.isRunning) 390 } 391 392 // TestFlagVTRecovery tests the disabled vtrecovery option. 393 func TestFlagVTRecovery(t *testing.T) { 394 tempDir, err := ioutil.TempDir(os.TempDir(), "sc") 395 assert.NoError(t, err) 396 defer func() { 397 if err := os.RemoveAll(tempDir); err != nil { 398 t.Fatalf("fail to delete file %v", err) 399 } 400 }() 401 402 info := prepare(t, func(info *testInfo) { 403 for i := 0; i < testTxCount; i++ { 404 ops[KLAY].request(info, info.localInfo) 405 } 406 }) 407 defer info.sim.Close() 408 409 vtr := NewValueTransferRecovery(&SCConfig{VTRecovery: true, VTRecoveryInterval: 60}, info.localInfo, info.remoteInfo) 410 vtr.Start() 411 assert.Equal(t, nil, vtr.WaitRunningStatus(true, 5*time.Second)) 412 vtr.Stop() 413 414 vtr = NewValueTransferRecovery(&SCConfig{VTRecovery: false}, info.localInfo, info.remoteInfo) 415 err = vtr.Start() 416 assert.Equal(t, ErrVtrDisabled, err) 417 vtr.Stop() 418 } 419 420 // TestAlreadyStartedVTRecovery tests the already started VTR error cases. 421 func TestAlreadyStartedVTRecovery(t *testing.T) { 422 tempDir, err := ioutil.TempDir(os.TempDir(), "sc") 423 assert.NoError(t, err) 424 defer func() { 425 if err := os.RemoveAll(tempDir); err != nil { 426 t.Fatalf("fail to delete file %v", err) 427 } 428 }() 429 info := prepare(t, func(info *testInfo) { 430 for i := 0; i < testTxCount; i++ { 431 ops[KLAY].request(info, info.localInfo) 432 } 433 }) 434 defer info.sim.Close() 435 436 vtr := NewValueTransferRecovery(&SCConfig{VTRecovery: true, VTRecoveryInterval: 60}, info.localInfo, info.remoteInfo) 437 err = vtr.Start() 438 assert.Equal(t, nil, err) 439 assert.Equal(t, nil, vtr.WaitRunningStatus(true, 5*time.Second)) 440 441 err = vtr.Start() 442 assert.Equal(t, ErrVtrAlreadyStarted, err) 443 444 vtr.Stop() 445 } 446 447 // TestScenarioMainChainRecovery tests the value transfer recovery of the parent chain to child chain value transfers. 448 func TestScenarioMainChainRecovery(t *testing.T) { 449 tempDir, err := ioutil.TempDir(os.TempDir(), "sc") 450 assert.NoError(t, err) 451 defer func() { 452 if err := os.RemoveAll(tempDir); err != nil { 453 t.Fatalf("fail to delete file %v", err) 454 } 455 }() 456 457 info := prepare(t, func(info *testInfo) { 458 for i := 0; i < testTxCount; i++ { 459 ops[KLAY].request(info, info.remoteInfo) 460 } 461 }) 462 defer info.sim.Close() 463 464 vtr := NewValueTransferRecovery(&SCConfig{VTRecovery: true}, info.localInfo, info.remoteInfo) 465 err = vtr.updateRecoveryHint() 466 if err != nil { 467 t.Fatal("fail to update a value transfer hint") 468 } 469 assert.NotEqual(t, vtr.parent2childHint.requestNonce, vtr.parent2childHint.handleNonce) 470 471 info.recoveryCh <- true 472 err = vtr.Recover() 473 if err != nil { 474 t.Fatal("fail to recover the value transfer") 475 } 476 ops[KLAY].dummyHandle(info, info.localInfo) 477 478 err = vtr.updateRecoveryHint() 479 if err != nil { 480 t.Fatal("fail to update a value transfer hint") 481 } 482 assert.Equal(t, vtr.parent2childHint.requestNonce, vtr.parent2childHint.handleNonce) 483 } 484 485 // TestScenarioAutomaticRecovery tests the recovery of the internal goroutine. 486 func TestScenarioAutomaticRecovery(t *testing.T) { 487 tempDir, err := ioutil.TempDir(os.TempDir(), "sc") 488 assert.NoError(t, err) 489 defer func() { 490 if err := os.RemoveAll(tempDir); err != nil { 491 t.Fatalf("fail to delete file %v", err) 492 } 493 }() 494 495 info := prepare(t, func(info *testInfo) { 496 for i := 0; i < testTxCount; i++ { 497 ops[KLAY].request(info, info.localInfo) 498 } 499 }) 500 defer info.sim.Close() 501 502 vtr := NewValueTransferRecovery(&SCConfig{VTRecovery: true, VTRecoveryInterval: 1}, info.localInfo, info.remoteInfo) 503 err = vtr.updateRecoveryHint() 504 if err != nil { 505 t.Fatal("fail to update a value transfer hint") 506 } 507 assert.NotEqual(t, vtr.child2parentHint.requestNonce, vtr.child2parentHint.handleNonce) 508 509 info.recoveryCh <- true 510 err = vtr.Start() 511 if err != nil { 512 t.Fatal("fail to start the value transfer") 513 } 514 assert.Equal(t, nil, vtr.WaitRunningStatus(true, 5*time.Second)) 515 ops[KLAY].dummyHandle(info, info.remoteInfo) 516 517 err = vtr.updateRecoveryHint() 518 if err != nil { 519 t.Fatal("fail to update a value transfer hint") 520 } 521 vtr.Stop() 522 assert.Equal(t, vtr.child2parentHint.requestNonce, vtr.child2parentHint.handleNonce) 523 } 524 525 // TestMultiOperatorRequestRecovery tests value transfer recovery for the multi-operator. 526 func TestMultiOperatorRequestRecovery(t *testing.T) { 527 tempDir, err := ioutil.TempDir(os.TempDir(), "sc") 528 assert.NoError(t, err) 529 defer func() { 530 if err := os.RemoveAll(tempDir); err != nil { 531 t.Fatalf("fail to delete file %v", err) 532 } 533 }() 534 535 // 1. Init dummy chain and do some value transfers. 536 info := prepare(t, func(info *testInfo) { 537 for i := 0; i < testTxCount; i++ { 538 ops[KLAY].request(info, info.localInfo) 539 } 540 }) 541 defer info.sim.Close() 542 543 // 2. Set multi-operator. 544 cAcc := info.nodeAuth 545 pAcc := info.chainAuth 546 opts := &bind.TransactOpts{From: cAcc.From, Signer: cAcc.Signer, GasLimit: testGasLimit} 547 _, err = info.localInfo.bridge.RegisterOperator(opts, pAcc.From) 548 assert.NoError(t, err) 549 opts = &bind.TransactOpts{From: pAcc.From, Signer: pAcc.Signer, GasLimit: testGasLimit} 550 _, err = info.remoteInfo.bridge.RegisterOperator(opts, cAcc.From) 551 assert.NoError(t, err) 552 info.sim.Commit() 553 554 // 3. Set operator threshold. 555 opts = &bind.TransactOpts{From: cAcc.From, Signer: cAcc.Signer, GasLimit: testGasLimit} 556 _, err = info.localInfo.bridge.SetOperatorThreshold(opts, voteTypeValueTransfer, 2) 557 assert.NoError(t, err) 558 opts = &bind.TransactOpts{From: pAcc.From, Signer: pAcc.Signer, GasLimit: testGasLimit} 559 _, err = info.remoteInfo.bridge.SetOperatorThreshold(opts, voteTypeValueTransfer, 2) 560 assert.NoError(t, err) 561 info.sim.Commit() 562 563 vtr := NewValueTransferRecovery(&SCConfig{VTRecovery: true}, info.localInfo, info.remoteInfo) 564 565 // 4. Update recovery hint. 566 err = vtr.updateRecoveryHint() 567 if err != nil { 568 t.Fatal("fail to update value transfer hint") 569 } 570 t.Log("value transfer hint", vtr.child2parentHint) 571 assert.Equal(t, uint64(testTxCount), vtr.child2parentHint.requestNonce) 572 assert.Equal(t, uint64(testTxCount-testPendingCount), vtr.child2parentHint.handleNonce) 573 574 // 5. Request events by using the hint. 575 err = vtr.retrievePendingEvents() 576 if err != nil { 577 t.Fatal("fail to retrieve pending events from the bridge contract") 578 } 579 580 // 6. Check pending events. 581 t.Log("check pending tx", "len", len(vtr.childEvents)) 582 count := 0 583 for _, ev := range vtr.childEvents { 584 assert.Equal(t, info.nodeAuth.From, ev.GetFrom()) 585 assert.Equal(t, info.aliceAuth.From, ev.GetTo()) 586 assert.Equal(t, big.NewInt(testAmount), ev.GetValueOrTokenId()) 587 assert.Condition(t, func() bool { 588 return uint64(testBlockOffset) <= ev.GetRaw().BlockNumber 589 }) 590 count++ 591 } 592 assert.Equal(t, testPendingCount, count) 593 594 // 7. Recover pending events 595 info.recoveryCh <- true 596 assert.Equal(t, nil, vtr.recoverPendingEvents()) 597 ops[KLAY].dummyHandle(info, info.remoteInfo) 598 599 // 8. Recover from the other operator (value transfer is not recovered yet). 600 err = vtr.updateRecoveryHint() 601 if err != nil { 602 t.Fatal("fail to update value transfer hint") 603 } 604 t.Log("value transfer hint", vtr.child2parentHint) 605 assert.Equal(t, uint64(testTxCount), vtr.child2parentHint.requestNonce) 606 assert.Equal(t, uint64(testTxCount-testPendingCount), vtr.child2parentHint.handleNonce) 607 608 err = vtr.retrievePendingEvents() 609 if err != nil { 610 t.Fatal("fail to retrieve pending events from the bridge contract") 611 } 612 assert.Equal(t, testPendingCount, len(vtr.childEvents)) 613 assert.Equal(t, nil, vtr.recoverPendingEvents()) 614 info.remoteInfo.account = info.localInfo.account // other operator 615 ops[KLAY].dummyHandle(info, info.remoteInfo) 616 617 // 9. Check results. 618 err = vtr.updateRecoveryHint() 619 if err != nil { 620 t.Fatal("fail to update value transfer hint") 621 } 622 err = vtr.retrievePendingEvents() 623 if err != nil { 624 t.Fatal("fail to retrieve pending events from the bridge contract") 625 } 626 assert.Equal(t, 0, len(vtr.childEvents)) 627 assert.Equal(t, nil, vtr.Recover()) // nothing to recover 628 } 629 630 // prepare generates dummy blocks for testing value transfer recovery. 631 func prepare(t *testing.T, vtcallback func(*testInfo)) *testInfo { 632 // Setup configuration. 633 config := &SCConfig{} 634 tempDir, err := ioutil.TempDir(os.TempDir(), "sc") 635 assert.NoError(t, err) 636 defer func() { 637 if err := os.RemoveAll(tempDir); err != nil { 638 t.Fatalf("fail to delete file %v", err) 639 } 640 }() 641 config.DataDir = tempDir 642 config.VTRecovery = true 643 644 bacc, err := NewBridgeAccounts(nil, config.DataDir, database.NewDBManager(&database.DBConfig{DBType: database.MemoryDB}), DefaultBridgeTxGasLimit, DefaultBridgeTxGasLimit) 645 assert.NoError(t, err) 646 bacc.pAccount.chainID = big.NewInt(0) 647 bacc.cAccount.chainID = big.NewInt(0) 648 649 cAcc := bacc.cAccount.GenerateTransactOpts() 650 pAcc := bacc.pAccount.GenerateTransactOpts() 651 652 // Generate a new random account and a funded simulator. 653 aliceKey, _ := crypto.GenerateKey() 654 aliceAuth := bind.NewKeyedTransactor(aliceKey) 655 656 // Alloc genesis and create a simulator. 657 alloc := blockchain.GenesisAlloc{ 658 cAcc.From: {Balance: big.NewInt(params.KLAY)}, 659 pAcc.From: {Balance: big.NewInt(params.KLAY)}, 660 aliceAuth.From: {Balance: big.NewInt(params.KLAY)}, 661 } 662 sim := backends.NewSimulatedBackend(alloc) 663 664 sc := &SubBridge{ 665 chainDB: database.NewDBManager(&database.DBConfig{DBType: database.MemoryDB}), 666 config: config, 667 peers: newBridgePeerSet(), 668 bridgeAccounts: bacc, 669 localBackend: sim, 670 remoteBackend: sim, 671 } 672 handler, err := NewSubBridgeHandler(sc) 673 if err != nil { 674 log.Fatalf("Failed to initialize the bridgeHandler : %v", err) 675 return nil 676 } 677 sc.handler = handler 678 sc.blockchain = sim.BlockChain() 679 680 // Prepare manager and deploy bridge contract. 681 bm, err := NewBridgeManager(sc) 682 localAddr, err := bm.DeployBridgeTest(sim, 10000, true) 683 assert.NoError(t, err) 684 remoteAddr, err := bm.DeployBridgeTest(sim, 10000, false) 685 assert.NoError(t, err) 686 687 localInfo, _ := bm.GetBridgeInfo(localAddr) 688 remoteInfo, _ := bm.GetBridgeInfo(remoteAddr) 689 sim.Commit() 690 691 // Prepare token contract 692 tokenLocalAddr, tx, tokenLocal, err := sctoken.DeployServiceChainToken(cAcc, sim, localAddr) 693 if err != nil { 694 log.Fatalf("Failed to DeployServiceChainToken: %v", err) 695 } 696 sim.Commit() 697 assert.Nil(t, bind.CheckWaitMined(sim, tx)) 698 699 tokenRemoteAddr, tx, tokenRemote, err := sctoken.DeployServiceChainToken(pAcc, sim, remoteAddr) 700 if err != nil { 701 log.Fatalf("Failed to DeployServiceChainToken: %v", err) 702 } 703 sim.Commit() 704 assert.Nil(t, bind.CheckWaitMined(sim, tx)) 705 706 testToken := big.NewInt(testChargeToken) 707 opts := &bind.TransactOpts{From: pAcc.From, Signer: pAcc.Signer, GasLimit: testGasLimit} 708 tx, err = tokenRemote.Transfer(opts, remoteAddr, testToken) 709 if err != nil { 710 log.Fatalf("Failed to Transfer for charging: %v", err) 711 } 712 sim.Commit() 713 assert.Nil(t, bind.CheckWaitMined(sim, tx)) 714 715 // Prepare NFT contract 716 nftLocalAddr, tx, nftLocal, err := scnft.DeployServiceChainNFT(cAcc, sim, localAddr) 717 if err != nil { 718 log.Fatalf("Failed to DeployServiceChainNFT: %v", err) 719 } 720 sim.Commit() 721 assert.Nil(t, bind.CheckWaitMined(sim, tx)) 722 723 nftRemoteAddr, tx, nftRemote, err := scnft.DeployServiceChainNFT(pAcc, sim, remoteAddr) 724 if err != nil { 725 log.Fatalf("Failed to DeployServiceChainNFT: %v", err) 726 } 727 sim.Commit() 728 assert.Nil(t, bind.CheckWaitMined(sim, tx)) 729 730 // Register tokens on the bridge 731 nodeOpts := &bind.TransactOpts{From: cAcc.From, Signer: cAcc.Signer, GasLimit: testGasLimit} 732 chainOpts := &bind.TransactOpts{From: pAcc.From, Signer: pAcc.Signer, GasLimit: testGasLimit} 733 tx, err = localInfo.bridge.RegisterToken(nodeOpts, tokenLocalAddr, tokenRemoteAddr) 734 sim.Commit() 735 assert.Nil(t, bind.CheckWaitMined(sim, tx)) 736 737 tx, err = localInfo.bridge.RegisterToken(nodeOpts, nftLocalAddr, nftRemoteAddr) 738 sim.Commit() 739 assert.Nil(t, bind.CheckWaitMined(sim, tx)) 740 741 tx, err = remoteInfo.bridge.RegisterToken(chainOpts, tokenRemoteAddr, tokenLocalAddr) 742 sim.Commit() 743 assert.Nil(t, bind.CheckWaitMined(sim, tx)) 744 745 tx, err = remoteInfo.bridge.RegisterToken(chainOpts, nftRemoteAddr, nftLocalAddr) 746 sim.Commit() 747 assert.Nil(t, bind.CheckWaitMined(sim, tx)) 748 749 // Register an NFT to chain account (minting) 750 for i := 0; i < testTxCount; i++ { 751 opts := &bind.TransactOpts{From: cAcc.From, Signer: cAcc.Signer, GasLimit: testGasLimit} 752 tx, err = nftLocal.MintWithTokenURI(opts, cAcc.From, big.NewInt(testNFT+int64(i)), "testURI") 753 assert.NoError(t, err) 754 sim.Commit() 755 assert.Nil(t, bind.CheckWaitMined(sim, tx)) 756 757 opts = &bind.TransactOpts{From: pAcc.From, Signer: pAcc.Signer, GasLimit: testGasLimit} 758 tx, err = nftRemote.MintWithTokenURI(opts, remoteAddr, big.NewInt(testNFT+int64(i)), "testURI") 759 assert.NoError(t, err) 760 sim.Commit() 761 assert.Nil(t, bind.CheckWaitMined(sim, tx)) 762 } 763 764 // Register the owner as a signer 765 _, err = localInfo.bridge.RegisterOperator(&bind.TransactOpts{From: cAcc.From, Signer: cAcc.Signer, GasLimit: testGasLimit}, cAcc.From) 766 assert.NoError(t, err) 767 _, err = remoteInfo.bridge.RegisterOperator(&bind.TransactOpts{From: pAcc.From, Signer: pAcc.Signer, GasLimit: testGasLimit}, pAcc.From) 768 assert.NoError(t, err) 769 sim.Commit() 770 771 // Subscribe events. 772 err = bm.SubscribeEvent(localAddr) 773 if err != nil { 774 t.Fatalf("local bridge manager event subscription failed") 775 } 776 err = bm.SubscribeEvent(remoteAddr) 777 if err != nil { 778 t.Fatalf("remote bridge manager event subscription failed") 779 } 780 781 // Prepare channel for event handling. 782 requestVTCh := make(chan RequestValueTransferEvent) 783 requestVTencodedCh := make(chan RequestValueTransferEncodedEvent) 784 handleVTCh := make(chan *HandleValueTransferEvent) 785 recoveryCh := make(chan bool) 786 bm.SubscribeReqVTev(requestVTCh) 787 bm.SubscribeReqVTencodedEv(requestVTencodedCh) 788 bm.SubscribeHandleVTev(handleVTCh) 789 790 info := testInfo{ 791 t, sim, sc, bm, localInfo, remoteInfo, 792 tokenLocalAddr, tokenRemoteAddr, tokenLocal, tokenRemote, 793 nftLocalAddr, nftRemoteAddr, nftLocal, nftRemote, 794 cAcc, pAcc, aliceAuth, recoveryCh, 795 sync.Mutex{}, 796 testNFT, 797 } 798 799 // Start a event handling loop. 800 wg := sync.WaitGroup{} 801 wg.Add((2 * testTxCount) - testPendingCount) 802 isRecovery := false 803 reqHandler := func(ev IRequestValueTransferEvent) { 804 t.Log("request value transfer", "nonce", ev.GetRequestNonce()) 805 if ev.GetRequestNonce() >= (testTxCount - testPendingCount) { 806 t.Log("missing handle value transfer", "nonce", ev.GetRequestNonce()) 807 } else { 808 switch ev.GetTokenType() { 809 case KLAY, ERC20, ERC721: 810 break 811 default: 812 t.Errorf("received ev.TokenType is unknown: %v", ev.GetTokenType()) 813 return 814 } 815 816 if ev.GetRaw().Address == info.localInfo.address { 817 ops[ev.GetTokenType()].handle(&info, info.remoteInfo, ev) 818 } else { 819 ops[ev.GetTokenType()].handle(&info, info.localInfo, ev) 820 } 821 } 822 } 823 go func() { 824 for { 825 select { 826 case ev := <-recoveryCh: 827 isRecovery = ev 828 case ev := <-requestVTCh: 829 reqHandler(ev) 830 if !isRecovery { 831 wg.Done() 832 } 833 case ev := <-requestVTencodedCh: 834 reqHandler(ev) 835 if !isRecovery { 836 wg.Done() 837 } 838 case ev := <-handleVTCh: 839 t.Log("handle value transfer", "nonce", ev.HandleNonce) 840 if !isRecovery { 841 wg.Done() 842 } 843 } 844 } 845 }() 846 847 // Request value transfer. 848 vtcallback(&info) 849 WaitGroupWithTimeOut(&wg, testTimeout, t) 850 851 return &info 852 } 853 854 func requestKLAYTransfer(info *testInfo, bi *BridgeInfo) { 855 bi.account.Lock() 856 defer bi.account.UnLock() 857 858 opts := bi.account.GenerateTransactOpts() 859 opts.Value = big.NewInt(testAmount) 860 tx, err := bi.bridge.RequestKLAYTransfer(opts, info.aliceAuth.From, big.NewInt(testAmount), nil) 861 if err != nil { 862 log.Fatalf("Failed to RequestKLAYTransfer: %v", err) 863 } 864 info.sim.Commit() 865 assert.Nil(info.t, bind.CheckWaitMined(info.sim, tx)) 866 } 867 868 func handleKLAYTransfer(info *testInfo, bi *BridgeInfo, ev IRequestValueTransferEvent) { 869 bi.account.Lock() 870 defer bi.account.UnLock() 871 872 assert.Equal(info.t, new(big.Int).SetUint64(testAmount), ev.GetValueOrTokenId()) 873 opts := bi.account.GenerateTransactOpts() 874 tx, err := bi.bridge.HandleKLAYTransfer(opts, ev.GetRaw().TxHash, ev.GetFrom(), ev.GetTo(), ev.GetValueOrTokenId(), ev.GetRequestNonce(), ev.GetRaw().BlockNumber, ev.GetExtraData()) 875 if err != nil { 876 log.Fatalf("\tFailed to HandleKLAYTransfer: %v", err) 877 } 878 info.sim.Commit() 879 assert.Nil(info.t, bind.CheckWaitMined(info.sim, tx)) 880 } 881 882 // TODO-Klaytn-ServiceChain: use ChildChainEventHandler 883 func dummyHandleRequestKLAYTransfer(info *testInfo, bi *BridgeInfo) { 884 for _, ev := range bi.GetPendingRequestEvents() { 885 handleKLAYTransfer(info, bi, ev.(RequestValueTransferEvent)) 886 } 887 info.sim.Commit() 888 } 889 890 func requestTokenTransfer(info *testInfo, bi *BridgeInfo) { 891 bi.account.Lock() 892 defer bi.account.UnLock() 893 894 var tx *types.Transaction 895 896 testToken := big.NewInt(testToken) 897 opts := bi.account.GenerateTransactOpts() 898 899 var err error 900 if bi.onChildChain { 901 tx, err = info.tokenLocalBridge.RequestValueTransfer(opts, testToken, info.chainAuth.From, big.NewInt(0), nil) 902 } else { 903 tx, err = info.tokenRemoteBridge.RequestValueTransfer(opts, testToken, info.nodeAuth.From, big.NewInt(0), nil) 904 } 905 906 if err != nil { 907 log.Fatalf("Failed to RequestValueTransfer for charging: %v", err) 908 } 909 info.sim.Commit() 910 assert.Nil(info.t, bind.CheckWaitMined(info.sim, tx)) 911 } 912 913 func handleTokenTransfer(info *testInfo, bi *BridgeInfo, ev IRequestValueTransferEvent) { 914 bi.account.Lock() 915 defer bi.account.UnLock() 916 917 assert.Equal(info.t, new(big.Int).SetUint64(testToken), ev.GetValueOrTokenId()) 918 tx, err := bi.bridge.HandleERC20Transfer( 919 bi.account.GenerateTransactOpts(), ev.GetRaw().TxHash, ev.GetFrom(), ev.GetTo(), info.tokenRemoteAddr, ev.GetValueOrTokenId(), ev.GetRequestNonce(), ev.GetRaw().BlockNumber, ev.GetExtraData()) 920 if err != nil { 921 log.Fatalf("Failed to HandleERC20Transfer: %v", err) 922 } 923 info.sim.Commit() 924 assert.Nil(info.t, bind.CheckWaitMined(info.sim, tx)) 925 } 926 927 // TODO-Klaytn-ServiceChain: use ChildChainEventHandler 928 func dummyHandleRequestTokenTransfer(info *testInfo, bi *BridgeInfo) { 929 for _, ev := range bi.GetPendingRequestEvents() { 930 handleTokenTransfer(info, bi, ev.(RequestValueTransferEvent)) 931 } 932 info.sim.Commit() 933 } 934 935 func requestNFTTransfer(info *testInfo, bi *BridgeInfo) { 936 bi.account.Lock() 937 defer bi.account.UnLock() 938 939 var tx *types.Transaction 940 941 opts := bi.account.GenerateTransactOpts() 942 // TODO-Klaytn need to separate child / parent chain nftIndex. 943 nftIndex := new(big.Int).SetInt64(info.nftIndex) 944 945 var err error 946 if bi.onChildChain { 947 tx, err = info.nftLocalBridge.RequestValueTransfer(opts, nftIndex, info.aliceAuth.From, nil) 948 } else { 949 tx, err = info.nftRemoteBridge.RequestValueTransfer(opts, nftIndex, info.aliceAuth.From, nil) 950 } 951 952 if err != nil { 953 log.Fatalf("Failed to requestNFTTransfer for charging: %v", err) 954 } 955 info.nftIndex++ 956 info.sim.Commit() 957 assert.Nil(info.t, bind.CheckWaitMined(info.sim, tx)) 958 } 959 960 func handleNFTTransfer(info *testInfo, bi *BridgeInfo, ev IRequestValueTransferEvent) { 961 bi.account.Lock() 962 defer bi.account.UnLock() 963 964 var nftAddr common.Address 965 966 if bi.onChildChain { 967 nftAddr = info.nftLocalAddr 968 } else { 969 nftAddr = info.nftRemoteAddr 970 } 971 uri := GetURI(ev) 972 tx, err := bi.bridge.HandleERC721Transfer( 973 bi.account.GenerateTransactOpts(), 974 ev.GetRaw().TxHash, ev.GetFrom(), ev.GetTo(), nftAddr, ev.GetValueOrTokenId(), 975 ev.GetRequestNonce(), ev.GetRaw().BlockNumber, uri, ev.GetExtraData()) 976 if err != nil { 977 log.Fatalf("Failed to handleERC721Transfer: %v", err) 978 } 979 info.sim.Commit() 980 assert.Nil(info.t, bind.CheckWaitMined(info.sim, tx)) 981 } 982 983 // TODO-Klaytn-ServiceChain: use ChildChainEventHandler 984 func dummyHandleRequestNFTTransfer(info *testInfo, bi *BridgeInfo) { 985 for _, ev := range bi.GetPendingRequestEvents() { 986 handleNFTTransfer(info, bi, ev) 987 } 988 info.sim.Commit() 989 }