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