github.com/iotexproject/iotex-core@v1.14.1-rc1/e2etest/local_transfer_test.go (about) 1 // Copyright (c) 2019 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package e2etest 7 8 import ( 9 "context" 10 "fmt" 11 "math/big" 12 "math/rand" 13 "testing" 14 "time" 15 16 "github.com/cenkalti/backoff" 17 "github.com/pkg/errors" 18 "github.com/stretchr/testify/require" 19 "google.golang.org/genproto/googleapis/rpc/errdetails" 20 "google.golang.org/grpc" 21 "google.golang.org/grpc/codes" 22 "google.golang.org/grpc/status" 23 24 "github.com/iotexproject/go-pkgs/crypto" 25 "github.com/iotexproject/iotex-address/address" 26 "github.com/iotexproject/iotex-proto/golang/iotexapi" 27 28 "github.com/iotexproject/iotex-core/action" 29 "github.com/iotexproject/iotex-core/action/protocol" 30 "github.com/iotexproject/iotex-core/action/protocol/account" 31 accountutil "github.com/iotexproject/iotex-core/action/protocol/account/util" 32 "github.com/iotexproject/iotex-core/action/protocol/rewarding" 33 "github.com/iotexproject/iotex-core/actpool" 34 "github.com/iotexproject/iotex-core/blockchain" 35 "github.com/iotexproject/iotex-core/blockchain/blockdao" 36 "github.com/iotexproject/iotex-core/blockchain/filedao" 37 "github.com/iotexproject/iotex-core/blockchain/genesis" 38 "github.com/iotexproject/iotex-core/config" 39 "github.com/iotexproject/iotex-core/db" 40 "github.com/iotexproject/iotex-core/pkg/probe" 41 "github.com/iotexproject/iotex-core/pkg/unit" 42 "github.com/iotexproject/iotex-core/server/itx" 43 "github.com/iotexproject/iotex-core/state/factory" 44 "github.com/iotexproject/iotex-core/test/identityset" 45 "github.com/iotexproject/iotex-core/testutil" 46 "github.com/iotexproject/iotex-core/tools/util" 47 ) 48 49 type TransferState int 50 51 const ( 52 //This transfer should fail to be accepted into action pool 53 TsfFail TransferState = iota 54 //This transfer should be accepted into action pool, 55 //and later on be minted into block chain after block creating interval 56 TsfSuccess 57 //This transfer should be accepted into action pool, 58 //but will stay in action pool (not minted yet) 59 //until all the blocks with preceding nonce arrive 60 TsfPending 61 //This transfer should enable all the pending transfer in action pool be accepted 62 //into block chain. This happens when a transfer with the missing nonce arrives, 63 //filling the gap between minted blocks and pending blocks. 64 TsfFinal 65 ) 66 67 type AccountState int 68 69 const ( 70 //This account should be created on blockchain in run time with the given balance 71 AcntCreate AccountState = iota 72 //This account already exist, need to load the the key, address, balance to this test case 73 AcntExist 74 //This account doesnt exist on blockchain, but have a valid key and address 75 AcntNotRegistered 76 //This account doesnt exist, the address is not valid (a random byte string) 77 AcntBadAddr 78 ) 79 80 type simpleTransferTestCfg struct { 81 senderAcntState AccountState 82 senderPriKey crypto.PrivateKey 83 senderBalance *big.Int 84 recvAcntState AccountState 85 recvPriKey crypto.PrivateKey 86 recvBalance *big.Int 87 nonce uint64 88 amount *big.Int 89 payload []byte 90 gasLimit uint64 91 gasPrice *big.Int 92 expectedResult TransferState 93 expectedDesc string 94 message string 95 } 96 97 var ( 98 localKeys = []string{ 99 "fd26207d4657c422da8242686ba4f5066be11ffe9d342d37967f9538c44cebbf", 100 "012d7c684388ca7508fb3483f58e29a8de327b28097dd1d207116225307c98bf", 101 "0a653365c521592062fbbd3b8e1fc64a80b6199bce2b1dbac091955b5fe14125", 102 "0b3eb204a1641ea072505eec5161043e8c19bd039fad7f61e2180d4d396af45b", 103 "affad54ae2fd6f139c235439bebb9810ccdd016911113b220af6fd87c952b5bd", 104 "d260035a571390213c8521b73fff47b6fd8ce2474e37a2421bf1d4657e06e3ea", 105 "dee8d3dab8fbf36990608936241d1cc6f7d51663285919806eb05b1365dd62a3", 106 "d08769fb91911eed6156b1ea7dbb8adf3a68b1ed3b4b173074e7a67996d76c5d", 107 "29945a86884def518347585caaddcc9ac08c5d6ca614b8547625541b43adffe7", 108 "c8018d8a2ed602831c3435b03e33669d0f59e29c939764f1b11591175f2fe615", 109 } 110 // In the test case: 111 // - an account with "nil" private key will be created with 112 // keys, address, and initialized with the given balance. 113 // - an account with exiting private key will load exiting 114 // balance into test case. 115 getSimpleTransferTests = []simpleTransferTestCfg{ 116 { 117 AcntCreate, nil, big.NewInt(1000000), 118 AcntCreate, nil, big.NewInt(1000000), 119 1, big.NewInt(100), // nonce, amount 120 make([]byte, 100), //payload 121 uint64(200000), big.NewInt(1), // gasLimit, gasPrice 122 TsfSuccess, "", 123 "Normal transfer from an account with enough balance and gas", 124 }, 125 { 126 AcntCreate, nil, big.NewInt(232222), 127 AcntCreate, nil, big.NewInt(100000), 128 1, big.NewInt(222222), 129 make([]byte, 0), 130 uint64(200000), big.NewInt(1), 131 TsfSuccess, "", 132 "Transfer with just enough balance", 133 }, 134 { 135 AcntCreate, nil, big.NewInt(1000000), 136 AcntNotRegistered, nil, big.NewInt(1000000), 137 1, big.NewInt(100), // nonce, amount 138 make([]byte, 100), //payload 139 uint64(200000), big.NewInt(1), // gasLimit, gasPrice 140 TsfSuccess, "", 141 "Normal transfer to an address not created on block chain", 142 }, 143 { 144 AcntCreate, nil, big.NewInt(100000), 145 AcntCreate, nil, big.NewInt(100000), 146 1, big.NewInt(0), 147 make([]byte, 4), 148 uint64(200000), big.NewInt(1), 149 TsfSuccess, "", 150 "Transfer with 0 amount", 151 }, 152 { 153 AcntExist, identityset.PrivateKey(0), big.NewInt(100000), 154 AcntCreate, nil, big.NewInt(100000), 155 1, big.NewInt(100), 156 make([]byte, 4), 157 uint64(200000), big.NewInt(1), 158 TsfSuccess, "", 159 "Transfer with same nonce from a single sender 1", 160 }, 161 { 162 AcntExist, identityset.PrivateKey(1), big.NewInt(100000), 163 AcntCreate, nil, big.NewInt(100000), 164 2, big.NewInt(100), 165 make([]byte, 4), 166 uint64(200000), big.NewInt(1), 167 TsfPending, "", 168 "Transfer with a sequence of nonce from a single sender 1", 169 }, 170 { 171 AcntExist, identityset.PrivateKey(1), big.NewInt(100000), 172 AcntCreate, nil, big.NewInt(100000), 173 3, big.NewInt(100), 174 make([]byte, 4), 175 uint64(200000), big.NewInt(1), 176 TsfPending, "", 177 "Transfer with a sequence of nonce from a single sender 2", 178 }, 179 { 180 AcntExist, getLocalKey(0), big.NewInt(30000), 181 AcntCreate, nil, big.NewInt(100000), 182 2, big.NewInt(20000), 183 make([]byte, 0), 184 uint64(200000), big.NewInt(0), 185 TsfPending, "", 186 "Transfer to multiple accounts with not enough total balance 1", 187 }, 188 { 189 AcntExist, getLocalKey(0), big.NewInt(30000), 190 AcntCreate, nil, big.NewInt(100000), 191 3, big.NewInt(20000), 192 make([]byte, 4), 193 uint64(200000), big.NewInt(0), 194 TsfPending, "", 195 "Transfer to multiple accounts with not enough total balance 2", 196 }, 197 { 198 AcntCreate, nil, big.NewInt(1000000), 199 AcntBadAddr, nil, big.NewInt(1000000), 200 1, big.NewInt(100), // nonce, amount 201 make([]byte, 100), //payload 202 uint64(200000), big.NewInt(1), // gasLimit, gasPrice 203 TsfFail, "Unknown", 204 "Normal transfer to a bad address", 205 }, 206 { 207 AcntNotRegistered, nil, big.NewInt(1000000), 208 AcntCreate, nil, big.NewInt(1000000), 209 1, big.NewInt(100), // nonce, amount 210 make([]byte, 100), //payload 211 uint64(200000), big.NewInt(1), // gasLimit, gasPrice 212 TsfFail, action.ErrInsufficientFunds.Error(), 213 "Normal transfer from an address not created on block chain", 214 }, 215 { 216 AcntCreate, nil, big.NewInt(232221), 217 AcntCreate, nil, big.NewInt(100000), 218 1, big.NewInt(222222), 219 make([]byte, 0), 220 uint64(200000), big.NewInt(1), 221 TsfFail, action.ErrInsufficientFunds.Error(), 222 "Transfer with not enough balance", 223 }, 224 { 225 AcntCreate, nil, big.NewInt(232222), 226 AcntCreate, nil, big.NewInt(100000), 227 1, big.NewInt(222222), 228 make([]byte, 4), 229 uint64(200000), big.NewInt(1), 230 TsfFail, action.ErrInsufficientFunds.Error(), 231 "Transfer with not enough balance with payload", 232 }, 233 { 234 AcntCreate, nil, big.NewInt(100000), 235 AcntCreate, nil, big.NewInt(100000), 236 1, big.NewInt(-100), 237 make([]byte, 4), 238 uint64(200000), big.NewInt(1), 239 TsfFail, "negative value", 240 "Transfer with negative amount", 241 }, 242 { 243 AcntCreate, nil, big.NewInt(1000000), 244 AcntCreate, nil, big.NewInt(1000000), 245 1, big.NewInt(100), 246 make([]byte, 0), 247 uint64(1000), big.NewInt(1), 248 TsfFail, action.ErrIntrinsicGas.Error(), 249 "Transfer with not enough gas limit", 250 }, 251 { 252 AcntCreate, nil, big.NewInt(100000), 253 AcntCreate, nil, big.NewInt(100000), 254 0, big.NewInt(0), 255 make([]byte, 4), 256 uint64(200000), big.NewInt(1), 257 TsfFail, "nonce too low", 258 "Transfer with nonce 0", 259 }, 260 { 261 AcntExist, identityset.PrivateKey(0), big.NewInt(100000), 262 AcntCreate, nil, big.NewInt(100000), 263 1, big.NewInt(100), 264 make([]byte, 4), 265 uint64(200000), big.NewInt(1), 266 TsfFail, "nonce too low", 267 "Transfer with same nonce from a single sender 2", 268 }, 269 { 270 AcntExist, identityset.PrivateKey(1), big.NewInt(100000), 271 AcntCreate, nil, big.NewInt(100000), 272 1, big.NewInt(100), 273 make([]byte, 4), 274 uint64(200000), big.NewInt(1), 275 TsfFinal, "", 276 "Transfer with a sequence of nonce from a single sender 3", 277 }, 278 { 279 AcntExist, getLocalKey(0), big.NewInt(30000), 280 AcntCreate, nil, big.NewInt(100000), 281 1, big.NewInt(20000), 282 make([]byte, 4), 283 uint64(200000), big.NewInt(0), 284 TsfFinal, "", 285 "Transfer to multiple accounts with not enough total balance 3", 286 }, 287 } 288 ) 289 290 func TestLocalTransfer(t *testing.T) { 291 require := require.New(t) 292 293 testTriePath, err := testutil.PathOfTempFile("trie") 294 require.NoError(err) 295 testDBPath, err := testutil.PathOfTempFile("db") 296 require.NoError(err) 297 testIndexPath, err := testutil.PathOfTempFile("index") 298 require.NoError(err) 299 testBloomfilterIndexPath, err := testutil.PathOfTempFile("bloomfilterIndex") 300 require.NoError(err) 301 testSystemLogPath, err := testutil.PathOfTempFile("systemlog") 302 require.NoError(err) 303 testCandidateIndexPath, err := testutil.PathOfTempFile("candidateIndex") 304 require.NoError(err) 305 testContractStakeIndexPath, err := testutil.PathOfTempFile("contractStakeIndex") 306 require.NoError(err) 307 sgdIndexDBPath, err := testutil.PathOfTempFile("sgdIndex") 308 require.NoError(err) 309 310 defer func() { 311 testutil.CleanupPath(testTriePath) 312 testutil.CleanupPath(testDBPath) 313 testutil.CleanupPath(testIndexPath) 314 testutil.CleanupPath(testSystemLogPath) 315 testutil.CleanupPath(testBloomfilterIndexPath) 316 testutil.CleanupPath(testCandidateIndexPath) 317 testutil.CleanupPath(testContractStakeIndexPath) 318 testutil.CleanupPath(sgdIndexDBPath) 319 }() 320 321 networkPort := 4689 322 apiPort := testutil.RandomPort() 323 cfg, err := newTransferConfig(testDBPath, testTriePath, testIndexPath, sgdIndexDBPath, testBloomfilterIndexPath, testSystemLogPath, testCandidateIndexPath, testContractStakeIndexPath, networkPort, apiPort) 324 defer func() { 325 delete(cfg.Plugins, config.GatewayPlugin) 326 }() 327 require.NoError(err) 328 329 for i, tsfTest := range getSimpleTransferTests { 330 if tsfTest.senderAcntState == AcntCreate { 331 sk, err := crypto.GenerateKey() 332 require.NoError(err) 333 addr := sk.PublicKey().Address() 334 require.NotNil(addr) 335 cfg.Genesis.InitBalanceMap[addr.String()] = tsfTest.senderBalance.String() 336 getSimpleTransferTests[i].senderPriKey = sk 337 } 338 if tsfTest.recvAcntState == AcntCreate { 339 sk, err := crypto.GenerateKey() 340 require.NoError(err) 341 addr := sk.PublicKey().Address() 342 require.NotNil(addr) 343 cfg.Genesis.InitBalanceMap[addr.String()] = tsfTest.recvBalance.String() 344 getSimpleTransferTests[i].recvPriKey = sk 345 } 346 } 347 for i := 0; i < len(localKeys); i++ { 348 sk := getLocalKey(i) 349 addr := sk.PublicKey().Address() 350 require.NotNil(addr) 351 cfg.Genesis.InitBalanceMap[addr.String()] = "30000" 352 } 353 354 // create server 355 svr, err := itx.NewServer(cfg) 356 require.NoError(err) 357 358 // Create and start probe server 359 ctx := genesis.WithGenesisContext(context.Background(), cfg.Genesis) 360 probeSvr := probe.New(7788) 361 require.NoError(probeSvr.Start(ctx)) 362 363 // Start server 364 ctx, stopServer := context.WithCancel(ctx) 365 defer func() { 366 require.NoError(probeSvr.Stop(ctx)) 367 stopServer() 368 }() 369 370 go itx.StartServer(ctx, svr, probeSvr, cfg) 371 372 // target address for grpc connection. Default is "127.0.0.1:14014" 373 grpcAddr := fmt.Sprintf("127.0.0.1:%d", apiPort) 374 grpcctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 375 defer cancel() 376 conn, err := grpc.DialContext(grpcctx, grpcAddr, grpc.WithBlock(), grpc.WithInsecure()) 377 require.NoError(err) 378 defer conn.Close() 379 client := iotexapi.NewAPIServiceClient(conn) 380 381 chainID := cfg.Chain.ID 382 bc := svr.ChainService(chainID).Blockchain() 383 sf := svr.ChainService(chainID).StateFactory() 384 ap := svr.ChainService(chainID).ActionPool() 385 as := svr.APIServer(chainID) 386 387 for _, tsfTest := range getSimpleTransferTests { 388 senderPriKey, senderAddr, err := initStateKeyAddr(tsfTest.senderAcntState, tsfTest.senderPriKey, tsfTest.senderBalance, bc, sf) 389 require.NoError(err, tsfTest.message) 390 391 _, recvAddr, err := initStateKeyAddr(tsfTest.recvAcntState, tsfTest.recvPriKey, tsfTest.recvBalance, bc, sf) 392 require.NoError(err, tsfTest.message) 393 394 tsf, err := action.SignedTransfer(recvAddr, senderPriKey, tsfTest.nonce, tsfTest.amount, 395 tsfTest.payload, tsfTest.gasLimit, tsfTest.gasPrice) 396 require.NoError(err, tsfTest.message) 397 398 // wait 2 block time, retry 5 times 399 retryInterval := cfg.Genesis.BlockInterval * 2 / 5 400 bo := backoff.WithMaxRetries(backoff.NewConstantBackOff(retryInterval), 5) 401 err = backoff.Retry(func() error { 402 _, err := client.SendAction(context.Background(), &iotexapi.SendActionRequest{Action: tsf.Proto()}) 403 return err 404 }, bo) 405 switch tsfTest.expectedResult { 406 case TsfSuccess: 407 require.NoError(err, tsfTest.message) 408 // Wait long enough for a block to be minted, and check the balance of both 409 // sender and receiver. 410 var actInfo *iotexapi.ActionInfo 411 err := backoff.Retry(func() error { 412 var err error 413 tsfHash, err1 := tsf.Hash() 414 if err1 != nil { 415 return err1 416 } 417 actInfo, err = util.GetActionByActionHash(as.CoreService(), tsfHash) 418 if err != nil { 419 return err 420 } 421 return err 422 }, bo) 423 require.NoError(err, tsfTest.message) 424 require.Equal(tsfTest.nonce, actInfo.GetAction().GetCore().GetNonce(), tsfTest.message) 425 require.Equal(senderPriKey.PublicKey().Bytes(), actInfo.GetAction().SenderPubKey, tsfTest.message) 426 427 senderAddr1, err := address.FromString(senderAddr) 428 require.NoError(err) 429 newSenderState, _ := accountutil.AccountState(ctx, sf, senderAddr1) 430 minusAmount := big.NewInt(0).Sub(tsfTest.senderBalance, tsfTest.amount) 431 gasUnitPayloadConsumed := big.NewInt(0).Mul(big.NewInt(int64(action.TransferPayloadGas)), 432 big.NewInt(int64(len(tsfTest.payload)))) 433 gasUnitTransferConsumed := big.NewInt(int64(action.TransferBaseIntrinsicGas)) 434 gasUnitConsumed := big.NewInt(0).Add(gasUnitPayloadConsumed, gasUnitTransferConsumed) 435 gasConsumed := big.NewInt(0).Mul(gasUnitConsumed, tsfTest.gasPrice) 436 expectedSenderBalance := big.NewInt(0).Sub(minusAmount, gasConsumed) 437 require.Equal(expectedSenderBalance.String(), newSenderState.Balance.String(), tsfTest.message) 438 439 recvAddr1, err := address.FromString(recvAddr) 440 require.NoError(err) 441 newRecvState, err := accountutil.AccountState(ctx, sf, recvAddr1) 442 require.NoError(err) 443 expectedRecvrBalance := big.NewInt(0) 444 if tsfTest.recvAcntState == AcntNotRegistered { 445 expectedRecvrBalance.Set(tsfTest.amount) 446 } else { 447 expectedRecvrBalance.Add(tsfTest.recvBalance, tsfTest.amount) 448 } 449 require.Equal(expectedRecvrBalance.String(), newRecvState.Balance.String(), tsfTest.message) 450 case TsfFail: 451 // bad address will be in actpool, but won't pass validation in protocol execution 452 if tsfTest.recvAcntState != AcntBadAddr { 453 require.Error(err, tsfTest.message) 454 455 st, ok := status.FromError(err) 456 require.True(ok, tsfTest.message) 457 require.Equal(st.Code(), codes.Internal, tsfTest.message) 458 459 details := st.Details() 460 require.Equal(len(details), 1, tsfTest.message) 461 462 detail, ok := details[0].(*errdetails.BadRequest) 463 require.True(ok, tsfTest.message) 464 require.Equal(len(detail.FieldViolations), 1, tsfTest.message) 465 466 violation := detail.FieldViolations[0] 467 require.Equal(tsfTest.expectedDesc, violation.Description, tsfTest.message) 468 require.Equal(violation.Field, "Action rejected", tsfTest.message) 469 470 //The transfer should be rejected right after we inject it 471 //Wait long enough to make sure the failed transfer does not exit in either action pool or blockchain 472 err := backoff.Retry(func() error { 473 var err error 474 tsfHash, err1 := tsf.Hash() 475 if err1 != nil { 476 return err1 477 } 478 _, err = ap.GetActionByHash(tsfHash) 479 return err 480 }, bo) 481 require.Error(err, tsfTest.message) 482 } 483 tsfHash, err1 := tsf.Hash() 484 require.NoError(err1) 485 _, err = util.GetActionByActionHash(as.CoreService(), tsfHash) 486 require.Error(err, tsfTest.message) 487 488 if tsfTest.senderAcntState == AcntCreate || tsfTest.senderAcntState == AcntExist { 489 senderAddr1, err := address.FromString(senderAddr) 490 require.NoError(err) 491 newSenderState, _ := accountutil.AccountState(ctx, sf, senderAddr1) 492 require.Equal(tsfTest.senderBalance.String(), newSenderState.Balance.String()) 493 } 494 495 case TsfPending: 496 require.NoError(err, tsfTest.message) 497 //Need to wait long enough to make sure the pending transfer is not minted, only stay in action pool 498 err := backoff.Retry(func() error { 499 var err error 500 tsfHash, err1 := tsf.Hash() 501 if err1 != nil { 502 return err1 503 } 504 _, err = ap.GetActionByHash(tsfHash) 505 return err 506 }, bo) 507 require.NoError(err, tsfTest.message) 508 tsfHash, err1 := tsf.Hash() 509 require.NoError(err1) 510 _, err = util.GetActionByActionHash(as.CoreService(), tsfHash) 511 require.Error(err, tsfTest.message) 512 case TsfFinal: 513 require.NoError(err, tsfTest.message) 514 //After a blocked is minted, check all the pending transfers in action pool are cleared 515 //This checking procedure is simplified for this test case, because of the complexity of 516 //handling pending transfers. 517 time.Sleep(cfg.Genesis.BlockInterval + time.Second) 518 require.Equal(0, lenPendingActionMap(ap.PendingActionMap()), tsfTest.message) 519 520 default: 521 require.True(false, tsfTest.message) 522 523 } 524 } 525 } 526 527 // initStateKeyAddr, if the given private key is nil, 528 // creates key, address, and init the new account with given balance 529 // otherwise, calculate the the address, and load test with existing 530 // balance state. 531 func initStateKeyAddr( 532 accountState AccountState, 533 privateKey crypto.PrivateKey, 534 initBalance *big.Int, 535 bc blockchain.Blockchain, 536 sf factory.Factory, 537 ) (crypto.PrivateKey, string, error) { 538 retKey := privateKey 539 retAddr := "" 540 switch accountState { 541 case AcntCreate: 542 addr := retKey.PublicKey().Address() 543 if addr == nil { 544 return nil, "", errors.New("failed to get address") 545 } 546 retAddr = addr.String() 547 548 case AcntExist: 549 addr := retKey.PublicKey().Address() 550 if addr == nil { 551 return nil, "", errors.New("failed to get address") 552 } 553 retAddr = addr.String() 554 ctx := genesis.WithGenesisContext(context.Background(), bc.Genesis()) 555 existState, err := accountutil.AccountState(ctx, sf, addr) 556 if err != nil { 557 return nil, "", err 558 } 559 initBalance.Set(existState.Balance) 560 case AcntNotRegistered: 561 sk, err := crypto.GenerateKey() 562 if err != nil { 563 return nil, "", err 564 } 565 addr := sk.PublicKey().Address() 566 if addr == nil { 567 return nil, "", errors.New("failed to get address") 568 } 569 retAddr = addr.String() 570 retKey = sk 571 case AcntBadAddr: 572 rand.Seed(time.Now().UnixNano()) 573 b := make([]byte, 41) 574 for i := range b { 575 b[i] = byte(65 + rand.Intn(26)) 576 } 577 retAddr = string(b) 578 } 579 return retKey, retAddr, nil 580 } 581 582 func getLocalKey(i int) crypto.PrivateKey { 583 sk, _ := crypto.HexStringToPrivateKey(localKeys[i]) 584 return sk 585 } 586 587 func newTransferConfig( 588 chainDBPath, 589 trieDBPath, 590 indexDBPath string, 591 sgdIndexDBPath string, 592 bloomfilterIndex string, 593 systemLogDBPath string, 594 candidateIndexDBPath string, 595 contractstakeIndexDBPath string, 596 networkPort, 597 apiPort int, 598 ) (config.Config, error) { 599 600 cfg := config.Default 601 cfg.Plugins[config.GatewayPlugin] = true 602 cfg.Network.Port = networkPort 603 cfg.Chain.ID = 1 604 cfg.Chain.ChainDBPath = chainDBPath 605 cfg.Chain.TrieDBPath = trieDBPath 606 cfg.Chain.TrieDBPatchFile = "" 607 cfg.Chain.IndexDBPath = indexDBPath 608 cfg.Chain.SGDIndexDBPath = sgdIndexDBPath 609 cfg.Chain.BloomfilterIndexDBPath = bloomfilterIndex 610 cfg.System.SystemLogDBPath = systemLogDBPath 611 cfg.Chain.CandidateIndexDBPath = candidateIndexDBPath 612 cfg.Chain.ContractStakingIndexDBPath = contractstakeIndexDBPath 613 cfg.Chain.EnableAsyncIndexWrite = true 614 cfg.ActPool.MinGasPriceStr = "0" 615 cfg.Consensus.Scheme = config.StandaloneScheme 616 cfg.API.GRPCPort = apiPort 617 cfg.Genesis.BlockInterval = 800 * time.Millisecond 618 619 return cfg, nil 620 } 621 622 func lenPendingActionMap(acts map[string][]*action.SealedEnvelope) int { 623 l := 0 624 for _, part := range acts { 625 l += len(part) 626 } 627 return l 628 } 629 630 func TestEnforceChainID(t *testing.T) { 631 require := require.New(t) 632 633 testCase := []struct { 634 chainID uint32 635 nonce uint64 636 success bool 637 }{ 638 { 639 1, 1, true, // tx chainID = node chainID, height < KamchatkaHeight 640 }, 641 { 642 2, 2, true, // tx chainID != node chainID, height < KamchatkaHeight 643 }, 644 { 645 1, 3, true, // tx chainID = node chainID, height = KamchatkaHeight 646 }, 647 { 648 1, 4, true, // tx chainID = node chainID, height > KamchatkaHeight 649 }, 650 { 651 2, 5, false, // tx chainID != node chainID, height > KamchatkaHeight 652 }, 653 { 654 0, 5, true, // tx chainID = 0, height < QuebecHeight, OK 655 }, 656 { 657 0, 6, false, // tx chainID = 0, height = QuebecHeight, reject 658 }, 659 } 660 661 ctx := context.Background() 662 cfg := config.Default 663 cfg.Genesis.BlockGasLimit = uint64(100000) 664 cfg.Genesis.MidwayBlockHeight = 3 665 cfg.Genesis.QuebecBlockHeight = 7 666 registry := protocol.NewRegistry() 667 acc := account.NewProtocol(rewarding.DepositGas) 668 require.NoError(acc.Register(registry)) 669 factoryCfg := factory.GenerateConfig(cfg.Chain, cfg.Genesis) 670 sf, err := factory.NewFactory(factoryCfg, db.NewMemKVStore(), factory.RegistryOption(registry)) 671 require.NoError(err) 672 ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) 673 require.NoError(err) 674 store, err := filedao.NewFileDAOInMemForTest() 675 require.NoError(err) 676 blkMemDao := blockdao.NewBlockDAOWithIndexersAndCache(store, []blockdao.BlockIndexer{sf}, cfg.DB.MaxCacheSize) 677 bc := blockchain.NewBlockchain( 678 cfg.Chain, 679 cfg.Genesis, 680 blkMemDao, 681 factory.NewMinter(sf, ap), 682 ) 683 require.NoError(bc.Start(ctx)) 684 685 defer func() { 686 require.NoError(bc.Stop(ctx)) 687 }() 688 for i, c := range testCase { 689 tsf, err := action.NewTransfer( 690 uint64(i)+1, 691 big.NewInt(100), 692 identityset.Address(27).String(), 693 []byte{}, uint64(100000), 694 big.NewInt(1).Mul(big.NewInt(int64(i)+10), big.NewInt(unit.Qev)), 695 ) 696 require.NoError(err) 697 698 bd := &action.EnvelopeBuilder{} 699 elp1 := bd.SetAction(tsf). 700 SetChainID(c.chainID). 701 SetNonce(c.nonce). 702 SetGasLimit(100000). 703 SetGasPrice(big.NewInt(1).Mul(big.NewInt(int64(i)+10), big.NewInt(unit.Qev))).Build() 704 selp, err := action.Sign(elp1, identityset.PrivateKey(0)) 705 require.NoError(err) 706 707 // simulate API receives tx 708 selp1, err := (&action.Deserializer{}).SetEvmNetworkID(cfg.Chain.EVMNetworkID).ActionToSealedEnvelope(selp.Proto()) 709 require.NoError(err) 710 711 // mint block using received tx 712 require.NoError(ap.Add(context.Background(), selp1)) 713 blk, err := bc.MintNewBlock(testutil.TimestampNow()) 714 require.NoError(err) 715 require.NoError(bc.CommitBlock(blk)) 716 require.Equal(c.success, len(blk.Actions) == 1) 717 require.Equal(c.success, len(blk.Receipts) == 1) 718 719 // verify action has valid chainID 720 if c.success { 721 act := blk.Actions[0] 722 tsf, ok := act.Action().(*action.Transfer) 723 require.True(ok) 724 require.Equal(c.chainID, act.ChainID()) 725 require.Equal(c.chainID, tsf.ChainID()) 726 } 727 } 728 }