github.com/energicryptocurrency/go-energi@v1.1.7/core/energi_zerofee_test.go (about) 1 // Copyright 2019 The Energi Core Authors 2 // This file is part of the Energi Core library. 3 // 4 // The Energi Core 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 Energi Core 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 Energi Core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package core 18 19 import ( 20 "errors" 21 "math/big" 22 "strings" 23 "testing" 24 "time" 25 26 "github.com/energicryptocurrency/go-energi/accounts/abi" 27 "github.com/energicryptocurrency/go-energi/common" 28 "github.com/energicryptocurrency/go-energi/consensus/ethash" 29 "github.com/energicryptocurrency/go-energi/core/types" 30 "github.com/energicryptocurrency/go-energi/core/vm" 31 energi_abi "github.com/energicryptocurrency/go-energi/energi/abi" 32 energi_params "github.com/energicryptocurrency/go-energi/energi/params" 33 "github.com/energicryptocurrency/go-energi/ethdb" 34 "github.com/energicryptocurrency/go-energi/params" 35 36 "github.com/stretchr/testify/assert" 37 ) 38 39 func TestIsValidZeroFee(t *testing.T) { 40 t.Parallel() 41 // log.Root().SetHandler(log.StdoutHandler) 42 43 migration_abi, err := abi.JSON(strings.NewReader(energi_abi.Gen2MigrationABI)) 44 assert.Empty(t, err) 45 claimCall, err := migration_abi.Pack( 46 "claim", common.Big0, common.Address{}, uint8(0), common.Hash{}, common.Hash{}) 47 assert.Empty(t, err) 48 49 mnreg_abi, err := abi.JSON(strings.NewReader(energi_abi.IMasternodeRegistryV2ABI)) 50 assert.Empty(t, err) 51 heartbeatCall, err := mnreg_abi.Pack("heartbeat", common.Big1, common.Hash{}, common.Big0) 52 assert.Empty(t, err) 53 invalidateCall, err := mnreg_abi.Pack("invalidate", common.Address{}) 54 assert.Empty(t, err) 55 cpreg_abi, err := abi.JSON(strings.NewReader(energi_abi.ICheckpointRegistryABI)) 56 assert.Empty(t, err) 57 cpsignCall, err := cpreg_abi.Pack("sign", common.Address{}, []byte{}) 58 assert.Empty(t, err) 59 60 res := false 61 62 // 63 res = IsValidZeroFee(types.NewTransaction( 64 0, 65 common.Address{}, 66 common.Big0, 67 500000, 68 common.Big0, 69 []byte{}, 70 )) 71 assert.False(t, res, "Simple zero") 72 // 73 res = IsValidZeroFee(types.NewTransaction( 74 0, 75 energi_params.Energi_MigrationContract, 76 common.Big0, 77 500000, 78 common.Big0, 79 claimCall, 80 )) 81 assert.True(t, res, "Valid migration claim") 82 // 83 res = IsValidZeroFee(types.NewTransaction( 84 2, 85 energi_params.Energi_MigrationContract, 86 common.Big0, 87 500000, 88 common.Big0, 89 claimCall, 90 )) 91 assert.True(t, res, "Valid migration claim - other nonce") 92 93 res = IsValidZeroFee(types.NewTransaction( 94 0, 95 energi_params.Energi_MigrationContract, 96 common.Big1, 97 500000, 98 common.Big0, 99 claimCall, 100 )) 101 assert.False(t, res, "Invalid migration claim - amount") 102 103 res = IsValidZeroFee(types.NewTransaction( 104 0, 105 energi_params.Energi_MigrationContract, 106 common.Big0, 107 500001, 108 common.Big0, 109 claimCall, 110 )) 111 assert.False(t, res, "Invalid migration claim - gas") 112 113 res = IsValidZeroFee(types.NewTransaction( 114 0, 115 energi_params.Energi_MigrationContract, 116 common.Big0, 117 500000, 118 common.Big1, 119 claimCall, 120 )) 121 assert.False(t, res, "Invalid migration claim - gas price") 122 123 res = IsValidZeroFee(types.NewTransaction( 124 0, 125 energi_params.Energi_MasternodeRegistry, 126 common.Big0, 127 500000, 128 common.Big0, 129 claimCall, 130 )) 131 assert.False(t, res, "Invalid migration claim - dst") 132 133 res = IsValidZeroFee(types.NewTransaction( 134 0, 135 energi_params.Energi_MigrationContract, 136 common.Big0, 137 500000, 138 common.Big0, 139 heartbeatCall, 140 )) 141 assert.False(t, res, "Invalid migration claim - call") 142 // 143 res = IsValidZeroFee(types.NewTransaction( 144 0, 145 energi_params.Energi_MasternodeRegistry, 146 common.Big0, 147 10000, 148 common.Big0, 149 heartbeatCall, 150 )) 151 assert.True(t, res, "Valid MN heartbeat") 152 res = IsValidZeroFee(types.NewTransaction( 153 0, 154 energi_params.Energi_MasternodeRegistry, 155 common.Big0, 156 10000, 157 common.Big0, 158 claimCall, 159 )) 160 assert.False(t, res, "Invalid MN heartbeat - data") 161 res = IsValidZeroFee(types.NewTransaction( 162 0, 163 energi_params.Energi_MigrationContract, 164 common.Big0, 165 40000, 166 common.Big0, 167 heartbeatCall, 168 )) 169 assert.False(t, res, "Invalid MN heartbeat - dst") 170 // 171 res = IsValidZeroFee(types.NewTransaction( 172 0, 173 energi_params.Energi_MasternodeRegistry, 174 common.Big0, 175 10000, 176 common.Big0, 177 invalidateCall, 178 )) 179 assert.True(t, res, "Valid MN validate") 180 res = IsValidZeroFee(types.NewTransaction( 181 0, 182 energi_params.Energi_MasternodeRegistry, 183 common.Big0, 184 10000, 185 common.Big0, 186 claimCall, 187 )) 188 assert.False(t, res, "Invalid MN validate - data") 189 res = IsValidZeroFee(types.NewTransaction( 190 0, 191 energi_params.Energi_MigrationContract, 192 common.Big0, 193 40000, 194 common.Big0, 195 invalidateCall, 196 )) 197 assert.False(t, res, "Invalid MN validate - dst") 198 // 199 res = IsValidZeroFee(types.NewTransaction( 200 0, 201 energi_params.Energi_CheckpointRegistry, 202 common.Big0, 203 10000, 204 common.Big0, 205 cpsignCall, 206 )) 207 assert.True(t, res, "Valid MN CP sign") 208 res = IsValidZeroFee(types.NewTransaction( 209 0, 210 energi_params.Energi_CheckpointRegistry, 211 common.Big0, 212 10000, 213 common.Big0, 214 claimCall, 215 )) 216 assert.False(t, res, "Invalid MN CP sign - data") 217 } 218 219 //--- 220 221 type fakeSigner struct { 222 sender common.Address 223 } 224 225 func (s *fakeSigner) Sender(tx *types.Transaction) (common.Address, error) { 226 return s.sender, nil 227 } 228 func (sg *fakeSigner) SignatureValues(tx *types.Transaction, sig []byte) (r, s, v *big.Int, err error) { 229 return common.Big0, common.Big0, common.Big0, nil 230 } 231 func (s *fakeSigner) Hash(tx *types.Transaction) common.Hash { 232 return tx.Hash() 233 } 234 func (s *fakeSigner) Equal(types.Signer) bool { 235 return false 236 } 237 238 func TestZeroFeeProtectorMasternode(t *testing.T) { 239 t.Parallel() 240 // log.Root().SetHandler(log.StdoutHandler) 241 242 now := time.Now() // It can be fixed 243 adjust_time := time.Duration(0) 244 245 protector := newZeroFeeProtector() 246 protector.timeNow = func() time.Time { 247 return now.Add(adjust_time) 248 } 249 250 pool := &TxPool{} 251 252 signer := &fakeSigner{} 253 pool.signer = signer 254 255 testdb := ethdb.NewMemDatabase() 256 gspec := &Genesis{ 257 Config: params.TestnetChainConfig, 258 } 259 gspec.MustCommit(testdb) 260 engine := ethash.NewFaker() 261 262 chain, err := NewBlockChain( 263 testdb, nil, gspec.Config, 264 engine, vm.Config{}, nil) 265 assert.Empty(t, err) 266 defer chain.Stop() 267 pool.chain = chain 268 pool.currentState, _ = chain.State() 269 270 mn_active1 := common.HexToAddress("0x0000000000000000000000000000000022345678") 271 mn_active2 := common.HexToAddress("0x0000000000000000000000000000000022345679") 272 mn_inactive := common.HexToAddress("0x0000000000000000000000000000000022345680") 273 274 pool.currentState.SetState( 275 energi_params.Energi_MasternodeList, 276 mn_active1.Hash(), 277 mn_inactive.Hash(), 278 ) 279 pool.currentState.SetState( 280 energi_params.Energi_MasternodeList, 281 mn_active2.Hash(), 282 mn_inactive.Hash(), 283 ) 284 pool.currentState.SetCode( 285 energi_params.Energi_MasternodeRegistry, 286 // PUSH1 0 PUSH1 0 RETURN 287 []byte{0x60, 0x00, 0x60, 0x00, 0xF3}, 288 ) 289 290 mnreg_abi, err := abi.JSON(strings.NewReader(energi_abi.IMasternodeRegistryV2ABI)) 291 assert.Empty(t, err) 292 heartbeatCall, err := mnreg_abi.Pack("heartbeat", common.Big1, common.Hash{}, common.Big0) 293 assert.Empty(t, err) 294 invalidateCall, err := mnreg_abi.Pack("invalidate", common.Address{}) 295 assert.Empty(t, err) 296 cpreg_abi, _ := abi.JSON(strings.NewReader(energi_abi.ICheckpointRegistryABI)) 297 cpsignCall, err := cpreg_abi.Pack("sign", common.Address{}, []byte{}) 298 assert.Empty(t, err) 299 300 hbtx0 := types.NewTransaction( 301 1, energi_params.Energi_MasternodeRegistry, common.Big0, 100000, common.Big0, heartbeatCall) 302 invtx0 := types.NewTransaction( 303 1, energi_params.Energi_MasternodeRegistry, common.Big0, 100000, common.Big0, invalidateCall) 304 sigtx0 := types.NewTransaction( 305 1, energi_params.Energi_MasternodeRegistry, common.Big0, 100000, common.Big0, cpsignCall) 306 hbtx1 := types.NewTransaction( 307 1, energi_params.Energi_MasternodeRegistry, common.Big0, 100000, common.Big0, heartbeatCall) 308 invtx1 := types.NewTransaction( 309 1, energi_params.Energi_MasternodeRegistry, common.Big0, 100000, common.Big0, invalidateCall) 310 sigtx1 := types.NewTransaction( 311 1, energi_params.Energi_MasternodeRegistry, common.Big0, 100000, common.Big0, cpsignCall) 312 hbtx2 := types.NewTransaction( 313 1, energi_params.Energi_MasternodeRegistry, common.Big0, 100000, common.Big0, heartbeatCall) 314 invtx2 := types.NewTransaction( 315 1, energi_params.Energi_MasternodeRegistry, common.Big0, 100000, common.Big0, invalidateCall) 316 sigtx2 := types.NewTransaction( 317 1, energi_params.Energi_MasternodeRegistry, common.Big0, 100000, common.Big0, cpsignCall) 318 319 // Inactive MN 320 signer.sender = mn_inactive 321 errMsg := errors.New("err: zero-fee DoS desc: inactive MN found") 322 err = protector.checkDoS(pool, hbtx0) 323 assert.Equal(t, errMsg, err) 324 err = protector.checkDoS(pool, invtx0) 325 assert.Equal(t, errMsg, err) 326 err = protector.checkDoS(pool, sigtx0) 327 assert.Equal(t, errMsg, err) 328 329 // Active MN 330 signer.sender = mn_active1 331 err = protector.checkDoS(pool, hbtx1) 332 assert.Equal(t, nil, err) 333 err = protector.checkDoS(pool, invtx1) 334 assert.Equal(t, nil, err) 335 err = protector.checkDoS(pool, sigtx1) 336 assert.Equal(t, nil, err) 337 338 // Active MN repeat interval 339 signer.sender = mn_active1 340 errMsg = errors.New("err: zero-fee DoS desc: time interval is less than the required") 341 err = protector.checkDoS(pool, hbtx1) 342 assert.Equal(t, errMsg, err) 343 err = protector.checkDoS(pool, invtx1) 344 assert.Equal(t, errMsg, err) 345 err = protector.checkDoS(pool, sigtx1) 346 assert.Equal(t, errMsg, err) 347 348 // Active another MV 349 signer.sender = mn_active2 350 err = protector.checkDoS(pool, hbtx2) 351 assert.Equal(t, nil, err) 352 err = protector.checkDoS(pool, invtx2) 353 assert.Equal(t, nil, err) 354 err = protector.checkDoS(pool, sigtx2) 355 assert.Equal(t, nil, err) 356 357 // Active MN after invalidation period is over 358 adjust_time = time.Duration(5) * time.Minute 359 360 signer.sender = mn_active1 361 err = protector.checkDoS(pool, hbtx1) 362 assert.Equal(t, nil, err) 363 err = protector.checkDoS(pool, invtx1) 364 assert.Equal(t, nil, err) 365 err = protector.checkDoS(pool, sigtx1) 366 assert.Equal(t, errMsg, err) 367 368 signer.sender = mn_active2 369 err = protector.checkDoS(pool, hbtx2) 370 assert.Equal(t, nil, err) 371 err = protector.checkDoS(pool, invtx2) 372 assert.Equal(t, nil, err) 373 err = protector.checkDoS(pool, sigtx2) 374 assert.Equal(t, errMsg, err) 375 376 // Active MN after checkpoint period is over 377 adjust_time = time.Duration(10) * time.Minute 378 379 signer.sender = mn_active1 380 err = protector.checkDoS(pool, hbtx1) 381 assert.Equal(t, nil, err) 382 err = protector.checkDoS(pool, invtx1) 383 assert.Equal(t, nil, err) 384 err = protector.checkDoS(pool, sigtx1) 385 assert.Equal(t, nil, err) 386 387 signer.sender = mn_active2 388 err = protector.checkDoS(pool, hbtx2) 389 assert.Equal(t, nil, err) 390 err = protector.checkDoS(pool, invtx2) 391 assert.Equal(t, nil, err) 392 err = protector.checkDoS(pool, sigtx2) 393 assert.Equal(t, nil, err) 394 395 // Active MN after heartbeat period is over 396 adjust_time = time.Duration(20) * time.Minute 397 398 pool.currentState.SetCode( 399 energi_params.Energi_MasternodeRegistry, 400 // PUSH1 0 PUSH1 0 REVERT 401 []byte{0x60, 0x00, 0x60, 0x00, 0xFD}, 402 ) 403 404 errMsg = errors.New("err: zero-fee DoS desc: <nil>") 405 signer.sender = mn_active1 406 err = protector.checkDoS(pool, hbtx1) 407 assert.Equal(t, errMsg, err) 408 err = protector.checkDoS(pool, invtx1) 409 assert.Equal(t, errMsg, err) 410 err = protector.checkDoS(pool, sigtx1) 411 assert.Equal(t, errMsg, err) 412 413 signer.sender = mn_active2 414 err = protector.checkDoS(pool, hbtx2) 415 assert.Equal(t, errMsg, err) 416 err = protector.checkDoS(pool, invtx2) 417 assert.Equal(t, errMsg, err) 418 err = protector.checkDoS(pool, sigtx2) 419 assert.Equal(t, errMsg, err) 420 421 // Restore correct 422 pool.currentState.SetCode( 423 energi_params.Energi_MasternodeRegistry, 424 // PUSH1 0 PUSH1 0 RETURN 425 []byte{0x60, 0x00, 0x60, 0x00, 0xF3}, 426 ) 427 428 signer.sender = mn_active1 429 err = protector.checkDoS(pool, hbtx1) 430 assert.Equal(t, nil, err) 431 err = protector.checkDoS(pool, invtx1) 432 assert.Equal(t, nil, err) 433 err = protector.checkDoS(pool, sigtx1) 434 assert.Equal(t, nil, err) 435 436 signer.sender = mn_active2 437 err = protector.checkDoS(pool, hbtx2) 438 assert.Equal(t, nil, err) 439 err = protector.checkDoS(pool, invtx2) 440 assert.Equal(t, nil, err) 441 err = protector.checkDoS(pool, sigtx2) 442 assert.Equal(t, nil, err) 443 444 // Test automatic cleanup 445 signer.sender = mn_inactive 446 447 _ = protector.checkDoS(pool, hbtx0) 448 assert.Equal(t, 2, len(protector.mnHeartbeats)) 449 assert.Equal(t, 2, len(protector.mnInvalidations)) 450 assert.Equal(t, 2, len(protector.mnCheckpoints)) 451 452 adjust_time = time.Duration(21)*time.Minute + time.Second 453 _ = protector.checkDoS(pool, hbtx0) 454 assert.Equal(t, 0, len(protector.mnHeartbeats)) 455 assert.Equal(t, 0, len(protector.mnInvalidations)) 456 assert.Equal(t, 2, len(protector.mnCheckpoints)) 457 458 adjust_time = time.Duration(30) * time.Minute 459 _ = protector.checkDoS(pool, hbtx0) 460 assert.Equal(t, 0, len(protector.mnHeartbeats)) 461 assert.Equal(t, 0, len(protector.mnInvalidations)) 462 assert.Equal(t, 2, len(protector.mnCheckpoints)) 463 464 adjust_time = time.Duration(30)*time.Minute + time.Second 465 protector.nextCleanup = now 466 _ = protector.checkDoS(pool, hbtx0) 467 assert.Equal(t, 0, len(protector.mnHeartbeats)) 468 assert.Equal(t, 0, len(protector.mnInvalidations)) 469 assert.Equal(t, 0, len(protector.mnCheckpoints)) 470 } 471 472 func TestZeroFeeProtectorMigration(t *testing.T) { 473 t.Parallel() 474 // log.Root().SetHandler(log.StdoutHandler) 475 476 now := time.Now() // It can be fixed 477 adjust_time := time.Duration(0) 478 479 protector := newZeroFeeProtector() 480 protector.timeNow = func() time.Time { 481 return now.Add(adjust_time) 482 } 483 484 pool := &TxPool{} 485 486 signer := &fakeSigner{} 487 pool.signer = signer 488 signer.sender = common.HexToAddress("0x0000000000000000000000000000000022345678") 489 490 testdb := ethdb.NewMemDatabase() 491 gspec := &Genesis{ 492 Config: params.TestnetChainConfig, 493 } 494 gspec.MustCommit(testdb) 495 engine := ethash.NewFaker() 496 497 chain, err := NewBlockChain( 498 testdb, nil, gspec.Config, 499 engine, vm.Config{}, nil) 500 assert.Empty(t, err) 501 defer chain.Stop() 502 pool.chain = chain 503 pool.currentState, _ = chain.State() 504 505 migration_abi, err := abi.JSON(strings.NewReader(energi_abi.Gen2MigrationABI)) 506 assert.Empty(t, err) 507 claim1Call, err := migration_abi.Pack( 508 "claim", big.NewInt(1), common.Address{}, uint8(0), common.Hash{}, common.Hash{}) 509 assert.Empty(t, err) 510 claim2Call, err := migration_abi.Pack( 511 "claim", big.NewInt(2), common.Address{}, uint8(0), common.Hash{}, common.Hash{}) 512 assert.Empty(t, err) 513 514 claim1 := types.NewTransaction( 515 1, energi_params.Energi_MigrationContract, common.Big0, 100000, common.Big0, claim1Call) 516 claim2 := types.NewTransaction( 517 1, energi_params.Energi_MigrationContract, common.Big0, 100000, common.Big0, claim2Call) 518 519 pool.currentState.SetCode( 520 energi_params.Energi_MigrationContract, 521 // PUSH1 1 PUSH1 0 MSTORE PUSH1 1 PUSH1 0 RETURN 522 []byte{0x60, 0x01, 0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xF3}, 523 ) 524 525 // Initial 526 err = protector.checkDoS(pool, claim1) 527 assert.Equal(t, nil, err) 528 err = protector.checkDoS(pool, claim2) 529 assert.Equal(t, nil, err) 530 531 // Before reset 532 adjust_time = time.Duration(2) * time.Minute 533 534 err = protector.checkDoS(pool, claim1) 535 errMsg := errors.New("migrationErr: zero-fee DoS desc: time interval is less than the required") 536 assert.Equal(t, errMsg, err) 537 err = protector.checkDoS(pool, claim2) 538 assert.Equal(t, errMsg, err) 539 540 // After reset + return 0 541 adjust_time = time.Duration(3) * time.Minute 542 543 err = protector.checkDoS(pool, claim1) 544 assert.Equal(t, nil, err) 545 546 pool.currentState.SetCode( 547 energi_params.Energi_MigrationContract, 548 // PUSH1 0 PUSH1 0 MSTORE PUSH1 1 PUSH1 0 RETURN 549 []byte{0x60, 0x00, 0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xF3}, 550 ) 551 552 errMsg = errors.New("migrationErr: zero-fee DoS desc: already claimed") 553 err = protector.checkDoS(pool, claim2) 554 assert.Equal(t, errMsg, err) 555 556 // After reset + revert 1 557 adjust_time = time.Duration(6) * time.Minute 558 559 pool.currentState.SetCode( 560 energi_params.Energi_MigrationContract, 561 // PUSH1 1 PUSH1 0 MSTORE PUSH1 1 PUSH1 0 RETURN 562 []byte{0x60, 0x01, 0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xF3}, 563 ) 564 565 err = protector.checkDoS(pool, claim1) 566 assert.Equal(t, nil, err) 567 568 pool.currentState.SetCode( 569 energi_params.Energi_MigrationContract, 570 // PUSH1 1 PUSH1 0 MSTORE PUSH1 1 PUSH1 0 REVERT 571 []byte{0x60, 0x01, 0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xFD}, 572 ) 573 574 errMsg = errors.New("migrationErr: zero-fee DoS desc: <nil>") 575 err = protector.checkDoS(pool, claim2) 576 assert.Equal(t, errMsg, err) 577 }