github.com/codingfuture/orig-energi3@v0.8.4/energi/service/checkpoints_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 service 18 19 import ( 20 "crypto/ecdsa" 21 "fmt" 22 "net" 23 "reflect" 24 "sync" 25 "testing" 26 "time" 27 28 "github.com/ethereum/go-ethereum/accounts/keystore" 29 "github.com/ethereum/go-ethereum/common" 30 "github.com/ethereum/go-ethereum/core" 31 "github.com/ethereum/go-ethereum/crypto" 32 "github.com/ethereum/go-ethereum/eth" 33 34 // "github.com/ethereum/go-ethereum/log" 35 "github.com/ethereum/go-ethereum/node" 36 "github.com/ethereum/go-ethereum/p2p" 37 "github.com/ethereum/go-ethereum/p2p/nat" 38 "github.com/ethereum/go-ethereum/params" 39 "github.com/stretchr/testify/assert" 40 41 energi_testutils "energi.world/core/gen3/energi/common/testutils" 42 energi_params "energi.world/core/gen3/energi/params" 43 ) 44 45 var sentCheckPoints = &testCheckPoints{} 46 47 type testCheckPoints struct { 48 mtx sync.RWMutex 49 cps []*core.CheckpointInfo 50 } 51 52 func (c *testCheckPoints) add(info *core.CheckpointInfo) { 53 c.mtx.Lock() 54 defer c.mtx.Unlock() 55 56 if len(c.cps) == 0 { 57 c.cps = []*core.CheckpointInfo{info} 58 return 59 } 60 61 // check for duplicates 62 for _, oldCps := range c.cps { 63 if reflect.DeepEqual(oldCps, info) { 64 return 65 } 66 } 67 c.cps = append(c.cps, info) 68 } 69 70 func (c *testCheckPoints) find(cp core.Checkpoint) bool { 71 c.mtx.RLock() 72 defer c.mtx.RUnlock() 73 74 for _, oldCps := range c.cps { 75 if oldCps.Checkpoint.Number == cp.Number && oldCps.Checkpoint.Hash == cp.Hash { 76 return true 77 } 78 } 79 return false 80 } 81 82 func TestCheckpointsService(t *testing.T) { 83 // log.Root().SetHandler(log.StdoutHandler) 84 85 // initialize tx Description Map 86 txDesc = txDescription{ 87 descMap: make(map[common.Hash]string), 88 } 89 90 withErr := func(msg string, err error) { 91 if err != nil { 92 panic(fmt.Errorf("%v error: %v", msg, err)) 93 } 94 } 95 96 delegatedPOS := []common.Address{ 97 energi_params.Energi_MigrationContract, 98 params.EnergiTestnetChainConfig.Energi.CPPSigner, 99 } 100 101 nodesInfo = make([]nodeConfig, 0, totalNetworkNodes) 102 signers = make(map[common.Address]*ecdsa.PrivateKey, totalNetworkNodes) 103 mnAddrToOwners = make(map[common.Address]*ecdsa.PrivateKey, 2) 104 allocs := core.DefaultPrealloc() 105 106 // generate private keys for all nodes. 107 for index := 0; index < totalNetworkNodes; index++ { 108 key, accAddr := accountGen() 109 signers[accAddr] = key 110 allocs[accAddr] = core.GenesisAccount{Balance: balance} 111 112 var isMasternode bool 113 // select masternodes 114 switch index { 115 case 0, 1, 2, 3, 4: // accounts at indexes 0 to 4 are masternodes. 116 isMasternode = true 117 118 // Create the masternode owners and pre-assign them a balance. 119 mnOwnerKey, mnOwnerAddr := accountGen() 120 mnAddrToOwners[accAddr] = mnOwnerKey 121 allocs[mnOwnerAddr] = core.GenesisAccount{Balance: balance} 122 123 default: // rest of the account belong to enodes. 124 isMasternode = false 125 } 126 127 nodesInfo = append(nodesInfo, nodeConfig{ 128 isMN: isMasternode, 129 address: accAddr, 130 }) 131 } 132 133 delPoSKeys := make([]*ecdsa.PrivateKey, 0, len(delegatedPOS)) 134 delPoSAddr := make([]common.Address, 0, len(delegatedPOS)) 135 // Map signer addresses to existing node private keys for signer accounts. 136 for _, addr := range delegatedPOS { 137 privKey, accAddr := accountGen() 138 allocs[accAddr] = core.GenesisAccount{Balance: balance} 139 140 delPoSAddr = append(delPoSAddr, accAddr) 141 delPoSKeys = append(delPoSKeys, privKey) 142 143 switch addr { 144 case energi_params.Energi_MigrationContract: 145 mgSigner = accAddr 146 147 case params.EnergiTestnetChainConfig.Energi.CPPSigner: 148 cpSigner = accAddr 149 } 150 } 151 152 for index := 0; index < totalNetworkNodes; index++ { 153 var err error 154 var node *node.Node 155 nConfig := nodesInfo[index] 156 key := signers[nConfig.address] 157 158 switch nConfig.isMN { 159 case true: 160 node, err = energiServices(key, allocs) 161 162 default: // rest of the account belong to enodes. 163 node, err = newNode(key, allocs) 164 } 165 166 msg := fmt.Sprintf("Creating node with Address: %v failed", nConfig.address.Hash().String()) 167 withErr(msg, err) 168 169 // Now assign the node to the node config. 170 nodesInfo[index].stack = node 171 } 172 173 // Add the delegetedPoS Addresses to the signer map 174 for i, addr := range delPoSAddr { 175 signers[addr] = delPoSKeys[i] 176 } 177 178 // Add the masternode owners 179 for _, ownerKey := range mnAddrToOwners { 180 ownerAddr := crypto.PubkeyToAddress(ownerKey.PublicKey) 181 signers[ownerAddr] = ownerKey 182 } 183 184 migrations := energi_testutils.NewTestGen2Migration() 185 // Create a gen2 migration tempfile 186 err := migrations.PrepareTestGen2Migration(params.EnergiTestnetChainConfig.ChainID.Uint64()) 187 withErr("Creating the Gen2 snapshot failed", err) 188 189 migrationFile = migrations.TempFileName() 190 191 injectAccount := func(store *keystore.KeyStore, privKey *ecdsa.PrivateKey) { 192 account, err := store.ImportECDSA(privKey, accountPass) 193 withErr("Failed to Inject new account", err) 194 195 // Unlock the account for staking 196 err = store.Unlock(account, accountPass, true) 197 withErr("Failed to Unlock new account for staking", err) 198 } 199 200 // Boot up the entire protocol while importing the accounts into respective nodes. 201 for _, data := range nodesInfo { 202 err = data.stack.Start() 203 withErr("Failed to start the protocol stack", err) 204 205 srv := data.stack.Server() 206 addr, _ := net.ResolveUDPAddr("udp", srv.ListenAddr) 207 conn, _ := net.ListenUDP("udp", addr) 208 realAdr := conn.LocalAddr().(*net.UDPAddr) 209 quit := make(chan struct{}) 210 if !realAdr.IP.IsLoopback() && srv.NAT != nil { 211 go nat.Map(srv.NAT, quit, "udp", realAdr.Port, realAdr.Port, "ethereum discovery") 212 } 213 214 // trigger external IP Address to be set. 215 srv.NAT.ExternalIP() 216 217 var ethService *eth.Ethereum 218 data.stack.Service(ðService) 219 220 store := data.stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) 221 // inject the main node personal account 222 injectAccount(store, signers[data.address]) 223 224 // Add delegated POS accounts to every node. 225 for i, addr := range delPoSAddr { 226 injectAccount(store, signers[addr]) 227 228 contractAddr := delegatedPOS[i] 229 ethService.AddDPoS(contractAddr, crypto.PubkeyToAddress(signers[addr].PublicKey)) 230 } 231 } 232 233 listenToCheckpointsTest(t) 234 235 // Clean Up 236 migrations.CleanUp() 237 238 // Stop the entire protocol for all nodesInfo. 239 for _, data := range nodesInfo { 240 err = data.stack.Stop() 241 withErr("Failed to stop the protocol stack", err) 242 } 243 } 244 245 // networkEvents receives all new changes that are mdae to the network. 246 func networkEvents( 247 quitCh chan struct{}, 248 isSignedCPP chan struct{}, 249 ethService *eth.Ethereum, 250 cpService *CheckpointService, 251 ) { 252 bc := ethService.BlockChain() 253 txpool := ethService.TxPool() 254 255 chainHeadCh := make(chan core.ChainHeadEvent, chainHeadChanSize) 256 headSub := bc.SubscribeChainHeadEvent(chainHeadCh) 257 defer headSub.Unsubscribe() 258 259 txEventCh := make(chan core.NewTxsEvent, 10) 260 txSub := txpool.SubscribeNewTxsEvent(txEventCh) 261 defer txSub.Unsubscribe() 262 263 evt := cpService.eth.EventMux().Subscribe(CheckpointProposalEvent{}) 264 defer evt.Unsubscribe() 265 266 //--- 267 for { 268 select { 269 case <-quitCh: 270 return 271 case ev := <-chainHeadCh: 272 fmt.Println(" _____ New Block Mined _____") 273 274 for _, tx := range ev.Block.Transactions() { 275 fmt.Printf("\t BlockNo: %v, Tx Desc: %v, Tx Hash: %v, Nonce: %v, GasPrice: %v, Gas: %v, To Address: %v \n", 276 ev.Block.Number(), checkTxDesc(tx), tx.Hash().String(), tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To().Hash().String()) 277 } 278 case txEvent := <-txEventCh: 279 for _, tx := range txEvent.Txs { 280 fmt.Printf("\t\t _____ (%s) Tx Announced %v _____ \n", checkTxDesc(tx), tx.Hash().String()) 281 } 282 283 case ev := <-evt.Chan(): 284 if ev == nil { 285 return 286 } 287 switch ev.Data.(type) { 288 case CheckpointProposalEvent: 289 if sentCheckPoints.find(ev.Data.(CheckpointProposalEvent).Checkpoint) { 290 isSignedCPP <- struct{}{} 291 } 292 } 293 294 break 295 296 // Shutdown 297 case <-headSub.Err(): 298 return 299 case <-txSub.Err(): 300 return 301 } 302 } 303 } 304 305 func listenToCheckpointsTest(t *testing.T) { 306 miningTimeout := time.After(miningInterval) 307 308 // masternode mn node picked is at index 1. 309 mn := nodesInfo[mnIndex] 310 var mnEthService *eth.Ethereum 311 mn.stack.Service(&mnEthService) 312 313 var cpServ *CheckpointService 314 mn.stack.Service(&cpServ) 315 316 quitChan := make(chan struct{}, 1) 317 isCPPChan := make(chan struct{}, 1) 318 // Listen to the network events 319 go networkEvents(quitChan, isCPPChan, mnEthService, cpServ) 320 321 mnServer := mn.stack.Server() 322 peerCh := make(chan *p2p.PeerEvent) 323 peerSub := mnServer.SubscribeEvents(peerCh) 324 defer close(peerCh) 325 defer peerSub.Unsubscribe() 326 327 // EnableMsg Events. 328 mnServer.EnableMsgEvents = true 329 330 fmt.Println(" _______ ADDING PEERS _____") 331 var peers int 332 // Add all nodes as peers then start mining in each peer 333 for count, data := range nodesInfo { 334 if count == mnIndex { 335 // Do not add the main masternode as a peer to itself. 336 continue 337 } 338 339 mnServer.AddPeer(data.stack.Server().Self()) 340 341 // This is a blocking operation that requires all nodes to be fully added 342 // as peers before further progress can be made. 343 peerConLoop: 344 for { 345 select { 346 case event := <-peerCh: 347 if event.Type == p2p.PeerEventTypeMsgRecv { 348 // Allow some delay for the peer to sync. 349 time.Sleep(peerSyncDelay) 350 break peerConLoop 351 } 352 case <-time.After(peerConInterval): 353 t.Fatal(errTimeout) 354 break peerConLoop 355 } 356 } 357 358 peers++ 359 } 360 361 // On subscription, peerCh has to always be read when full to allow other txs 362 // to be announced. 363 go func() { 364 waitLoop: 365 for { 366 select { 367 case <-peerCh: 368 case <-quitChan: 369 break waitLoop 370 } 371 } 372 }() 373 374 // Confirm that all the peers were added to the network. 375 assert.Equal(t, peers, mnServer.PeerCount()) 376 377 fmt.Println(" _______ START MINING _____") 378 // Add all nodes as peers then start mining in each peer 379 for _, data := range nodesInfo { 380 var ethService *eth.Ethereum 381 data.stack.Service(ðService) 382 383 go func() { 384 err := ethService.StartMining(2) 385 assert.Equal(t, nil, err) 386 if err != nil { 387 return 388 } 389 390 // If shutting down, exit this goroutine. 391 for range quitChan { 392 return 393 } 394 }() 395 } 396 397 fmt.Println(" _______ ACTIVATE MASTERNODES _____") 398 err := mnPrepare(nodesInfo) 399 assert.Equal(t, nil, err) 400 401 // The cpp signer node proposes a checkpoint. 402 fmt.Println(" _______ PROPOSE CHECKPOINT-1 _____") 403 cpInfo, err := cpPropose() 404 sentCheckPoints.add(cpInfo) 405 assert.Equal(t, nil, err) 406 407 // Send more txs. 408 fmt.Println(" _______ SEND MORE TXS _____") 409 err = sendMoreTxs(mn.stack.Server().PrivateKey) 410 assert.Equal(t, nil, err) 411 412 cppsigner := nodesInfo[cpSignerIndex] 413 var ethServ *eth.Ethereum 414 cppsigner.stack.Service(ðServ) 415 416 fmt.Println(" _______ CHECK TX POOL BEFORE WAITING _____") 417 { 418 // Tx pool according to the main masternode before test. 419 pending, queued := ethServ.TxPool().Content() 420 txPoolContents(pending, "(BEFORE) ___ CPP Signer Pending") 421 txPoolContents(queued, "(BEFORE) ____ CPP Signer queued") 422 } 423 424 { 425 // Tx pool according to the cpp signer node before test. 426 pending, queued := mnEthService.TxPool().Content() 427 txPoolContents(pending, "(BEFORE) ____ MN Pending") 428 txPoolContents(queued, "(BEFORE) ____ MN queued") 429 } 430 431 fmt.Println(" _______ PROPOSE CHECKPOINT-2 _____") 432 cpInfo, err = cpPropose() 433 sentCheckPoints.add(cpInfo) 434 assert.Equal(t, nil, err) 435 436 // Wait for a checkpoint signed by a masternode to be discovered or the max 437 // mining interval to expire. 438 439 select { 440 case <-isCPPChan: 441 // Test Passed 442 fmt.Println(" _______ A checkpoint event by the checkpoint service was found _____") 443 444 case <-miningTimeout: 445 // Test Failed 446 t.Fatalf(" _______ Checkpoint event NOT found: checkpoint service failed to send event on time _____") 447 } 448 449 // Now quit the listening of network events. 450 quitChan <- struct{}{} 451 452 fmt.Println(" _______ CHECK TX POOL AFTER WAITING _____") 453 { 454 // Tx pool according to the main masternode after test. 455 pending, queued := mnEthService.TxPool().Content() 456 txPoolContents(pending, "(AFTER) ____ MN Pending") 457 txPoolContents(queued, "(AFTER) ____ MN queued") 458 } 459 460 { 461 // Tx pool according to the cpp signer node after test. 462 pending, queued := ethServ.TxPool().Content() 463 txPoolContents(pending, "(AFTER) ____ CPP Signer Pending") 464 txPoolContents(queued, "(AFTER) ____ CPP Signer queued") 465 } 466 467 fmt.Println(" >>>>>>>> Checkpoint Service Test Complete <<<<<<<<<<") 468 }