github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/network/server_test.go (about) 1 package network 2 3 import ( 4 "errors" 5 "fmt" 6 "math/big" 7 "net" 8 "strconv" 9 "sync" 10 "sync/atomic" 11 "testing" 12 "time" 13 14 "github.com/nspcc-dev/neo-go/internal/fakechain" 15 "github.com/nspcc-dev/neo-go/internal/random" 16 "github.com/nspcc-dev/neo-go/pkg/config" 17 "github.com/nspcc-dev/neo-go/pkg/consensus" 18 "github.com/nspcc-dev/neo-go/pkg/core" 19 "github.com/nspcc-dev/neo-go/pkg/core/block" 20 "github.com/nspcc-dev/neo-go/pkg/core/interop" 21 "github.com/nspcc-dev/neo-go/pkg/core/mpt" 22 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 23 "github.com/nspcc-dev/neo-go/pkg/crypto/keys" 24 "github.com/nspcc-dev/neo-go/pkg/network/capability" 25 "github.com/nspcc-dev/neo-go/pkg/network/payload" 26 "github.com/nspcc-dev/neo-go/pkg/util" 27 "github.com/nspcc-dev/neo-go/pkg/vm/opcode" 28 "github.com/stretchr/testify/assert" 29 "github.com/stretchr/testify/require" 30 "go.uber.org/zap/zaptest" 31 ) 32 33 type fakeConsensus struct { 34 started atomic.Bool 35 stopped atomic.Bool 36 payloads []*payload.Extensible 37 txlock sync.Mutex 38 txs []*transaction.Transaction 39 } 40 41 var _ consensus.Service = (*fakeConsensus)(nil) 42 43 func (f *fakeConsensus) Name() string { return "fake" } 44 func (f *fakeConsensus) Start() { f.started.Store(true) } 45 func (f *fakeConsensus) Shutdown() { f.stopped.Store(true) } 46 func (f *fakeConsensus) OnPayload(p *payload.Extensible) error { 47 f.payloads = append(f.payloads, p) 48 return nil 49 } 50 func (f *fakeConsensus) OnTransaction(tx *transaction.Transaction) { 51 f.txlock.Lock() 52 defer f.txlock.Unlock() 53 f.txs = append(f.txs, tx) 54 } 55 func (f *fakeConsensus) GetPayload(h util.Uint256) *payload.Extensible { panic("implement me") } 56 57 func TestNewServer(t *testing.T) { 58 bc := &fakechain.FakeChain{Blockchain: config.Blockchain{ 59 ProtocolConfiguration: config.ProtocolConfiguration{ 60 P2PStateExchangeExtensions: true, 61 StateRootInHeader: true, 62 }}} 63 s, err := newServerFromConstructors(ServerConfig{}, bc, new(fakechain.FakeStateSync), nil, newFakeTransp, newTestDiscovery) 64 require.Error(t, err) 65 66 t.Run("set defaults", func(t *testing.T) { 67 s = newTestServer(t, ServerConfig{MinPeers: -1}) 68 69 require.True(t, s.ID() != 0) 70 require.Equal(t, defaultMinPeers, s.ServerConfig.MinPeers) 71 require.Equal(t, defaultMaxPeers, s.ServerConfig.MaxPeers) 72 require.Equal(t, defaultAttemptConnPeers, s.ServerConfig.AttemptConnPeers) 73 }) 74 t.Run("don't defaults", func(t *testing.T) { 75 cfg := ServerConfig{ 76 MinPeers: 1, 77 MaxPeers: 2, 78 AttemptConnPeers: 3, 79 } 80 s = newTestServer(t, cfg) 81 82 require.True(t, s.ID() != 0) 83 require.Equal(t, 1, s.ServerConfig.MinPeers) 84 require.Equal(t, 2, s.ServerConfig.MaxPeers) 85 require.Equal(t, 3, s.ServerConfig.AttemptConnPeers) 86 }) 87 } 88 89 func TestServerStartAndShutdown(t *testing.T) { 90 t.Run("no consensus", func(t *testing.T) { 91 s := newTestServer(t, ServerConfig{}) 92 93 s.Start() 94 p := newLocalPeer(t, s) 95 s.register <- p 96 require.Eventually(t, func() bool { return 1 == s.PeerCount() }, time.Second, time.Millisecond*10) 97 98 require.True(t, s.started.Load()) 99 require.Eventually(t, func() bool { 100 return s.transports[0].(*fakeTransp).started.Load() 101 }, 2*time.Second, 200*time.Millisecond) 102 assert.Nil(t, s.txCallback) 103 104 s.Shutdown() 105 106 require.False(t, s.started.Load()) 107 require.True(t, s.transports[0].(*fakeTransp).closed.Load()) 108 err, ok := p.droppedWith.Load().(error) 109 require.True(t, ok) 110 require.ErrorIs(t, err, errServerShutdown) 111 }) 112 t.Run("with consensus", func(t *testing.T) { 113 s := newTestServer(t, ServerConfig{}) 114 cons := new(fakeConsensus) 115 s.AddConsensusService(cons, cons.OnPayload, cons.OnTransaction) 116 117 s.Start() 118 p := newLocalPeer(t, s) 119 s.register <- p 120 require.Eventually(t, func() bool { return 1 == s.PeerCount() }, time.Second, time.Millisecond*10) 121 122 assert.True(t, s.services["fake"].(*fakeConsensus).started.Load()) 123 require.True(t, s.started.Load()) 124 125 s.Shutdown() 126 127 require.False(t, s.started.Load()) 128 require.True(t, s.services["fake"].(*fakeConsensus).stopped.Load()) 129 }) 130 t.Run("double start", func(t *testing.T) { 131 s := newTestServer(t, ServerConfig{}) 132 startWithCleanup(t, s) 133 134 // Attempt to start the server again. 135 s.Start() 136 137 require.True(t, s.started.Load(), "server should still be marked as started after second Start call") 138 }) 139 t.Run("double shutdown", func(t *testing.T) { 140 s := newTestServer(t, ServerConfig{}) 141 s.Start() 142 require.True(t, s.started.Load(), "server should still be marked as started after second Start call") 143 s.Shutdown() 144 145 require.False(t, s.started.Load(), "server should be marked as not started after second Shutdown call") 146 // Attempt to shutdown the server again. 147 s.Shutdown() 148 // Verify the server state remains unchanged and is still considered shutdown. 149 require.False(t, s.started.Load(), "server should remain shutdown after second call") 150 }) 151 } 152 153 func TestServerRegisterPeer(t *testing.T) { 154 const peerCount = 3 155 156 s := newTestServer(t, ServerConfig{MaxPeers: 2}) 157 ps := make([]*localPeer, peerCount) 158 for i := range ps { 159 ps[i] = newLocalPeer(t, s) 160 ps[i].netaddr.Port = i + 1 161 ps[i].version = &payload.Version{Nonce: uint32(i), UserAgent: []byte("fake")} 162 } 163 164 startWithCleanup(t, s) 165 166 s.register <- ps[0] 167 require.Eventually(t, func() bool { return 1 == s.PeerCount() }, time.Second, time.Millisecond*10) 168 s.handshake <- ps[0] 169 170 s.register <- ps[1] 171 s.handshake <- ps[1] 172 require.Eventually(t, func() bool { return 2 == s.PeerCount() }, time.Second, time.Millisecond*10) 173 174 require.Equal(t, 0, len(s.discovery.UnconnectedPeers())) 175 s.register <- ps[2] 176 require.Eventually(t, func() bool { return len(s.discovery.UnconnectedPeers()) > 0 }, time.Second, time.Millisecond*100) 177 178 index := -1 179 addrs := s.discovery.UnconnectedPeers() 180 for _, addr := range addrs { 181 for j := range ps { 182 if ps[j].PeerAddr().String() == addr { 183 index = j 184 break 185 } 186 } 187 } 188 require.True(t, index >= 0) 189 err, ok := ps[index].droppedWith.Load().(error) 190 require.True(t, ok) 191 require.ErrorIs(t, err, errMaxPeers) 192 193 index = (index + 1) % peerCount 194 s.unregister <- peerDrop{ps[index], errIdenticalID} 195 require.Eventually(t, func() bool { 196 bad := s.BadPeers() 197 for i := range bad { 198 if bad[i] == ps[index].PeerAddr().String() { 199 return true 200 } 201 } 202 return false 203 }, time.Second, time.Millisecond*50) 204 } 205 206 func TestGetBlocksByIndex(t *testing.T) { 207 testGetBlocksByIndex(t, CMDGetBlockByIndex) 208 } 209 210 func testGetBlocksByIndex(t *testing.T, cmd CommandType) { 211 s := newTestServer(t, ServerConfig{UserAgent: "/test/"}) 212 start := s.chain.BlockHeight() 213 if cmd == CMDGetHeaders { 214 start = s.chain.HeaderHeight() 215 s.stateSync.(*fakechain.FakeStateSync).RequestHeaders.Store(true) 216 } 217 ps := make([]*localPeer, 10) 218 expectsCmd := make([]CommandType, 10) 219 expectedHeight := make([][]uint32, 10) 220 for i := range ps { 221 i := i 222 ps[i] = newLocalPeer(t, s) 223 ps[i].messageHandler = func(t *testing.T, msg *Message) { 224 require.Equal(t, expectsCmd[i], msg.Command) 225 if expectsCmd[i] == cmd { 226 p, ok := msg.Payload.(*payload.GetBlockByIndex) 227 require.True(t, ok) 228 require.Contains(t, expectedHeight[i], p.IndexStart) 229 expectsCmd[i] = CMDPong 230 } else if expectsCmd[i] == CMDPong { 231 expectsCmd[i] = cmd 232 } 233 } 234 expectsCmd[i] = cmd 235 expectedHeight[i] = []uint32{start + 1} 236 } 237 go s.transports[0].Accept() 238 239 nonce := uint32(0) 240 checkPingRespond := func(t *testing.T, peerIndex int, peerHeight uint32, hs ...uint32) { 241 nonce++ 242 expectedHeight[peerIndex] = hs 243 require.NoError(t, s.handlePing(ps[peerIndex], payload.NewPing(peerHeight, nonce))) 244 } 245 246 // Send all requests for all chunks. 247 checkPingRespond(t, 0, 5000, 1) 248 checkPingRespond(t, 1, 5000, 1+payload.MaxHashesCount) 249 checkPingRespond(t, 2, 5000, 1+2*payload.MaxHashesCount) 250 checkPingRespond(t, 3, 5000, 1+3*payload.MaxHashesCount) 251 252 // Receive some blocks. 253 s.chain.(*fakechain.FakeChain).Blockheight.Store(2123) 254 255 // Minimum chunk has priority. 256 checkPingRespond(t, 5, 5000, 2124) 257 checkPingRespond(t, 6, 5000, 2624) 258 // Request minimal height for peers behind. 259 checkPingRespond(t, 7, 3100, 2124) 260 checkPingRespond(t, 8, 5000, 3124) 261 checkPingRespond(t, 9, 5000, 3624) 262 // Request random height after that. 263 checkPingRespond(t, 1, 5000, 2124, 2624, 3124, 3624) 264 checkPingRespond(t, 2, 5000, 2124, 2624, 3124, 3624) 265 checkPingRespond(t, 3, 5000, 2124, 2624, 3124, 3624) 266 } 267 268 func TestSendVersion(t *testing.T) { 269 var ( 270 s = newTestServer(t, ServerConfig{UserAgent: "/test/"}) 271 p = newLocalPeer(t, s) 272 ) 273 // we need to set listener at least to handle dynamic port correctly 274 s.transports[0].Accept() 275 p.messageHandler = func(t *testing.T, msg *Message) { 276 // listener is already set, so Addresses(nil) gives us proper address with port 277 _, prt := s.transports[0].HostPort() 278 port, err := strconv.ParseUint(prt, 10, 16) 279 assert.NoError(t, err) 280 assert.Equal(t, CMDVersion, msg.Command) 281 assert.IsType(t, msg.Payload, &payload.Version{}) 282 version := msg.Payload.(*payload.Version) 283 assert.NotZero(t, version.Nonce) 284 assert.Equal(t, 1, len(version.Capabilities)) 285 assert.ElementsMatch(t, []capability.Capability{ 286 { 287 Type: capability.TCPServer, 288 Data: &capability.Server{ 289 Port: uint16(port), 290 }, 291 }, 292 }, version.Capabilities) 293 assert.Equal(t, uint32(0), version.Version) 294 assert.Equal(t, []byte("/test/"), version.UserAgent) 295 } 296 297 require.NoError(t, p.SendVersion()) 298 } 299 300 // Server should reply with a verack after receiving a valid version. 301 func TestVerackAfterHandleVersionCmd(t *testing.T) { 302 var ( 303 s = newTestServer(t, ServerConfig{}) 304 p = newLocalPeer(t, s) 305 ) 306 na, _ := net.ResolveTCPAddr("tcp", "0.0.0.0:3000") 307 p.netaddr = *na 308 309 // Should have a verack 310 p.messageHandler = func(t *testing.T, msg *Message) { 311 assert.Equal(t, CMDVerack, msg.Command) 312 } 313 capabilities := []capability.Capability{ 314 { 315 Type: capability.FullNode, 316 Data: &capability.Node{ 317 StartHeight: 0, 318 }, 319 }, 320 { 321 Type: capability.TCPServer, 322 Data: &capability.Server{ 323 Port: 3000, 324 }, 325 }, 326 } 327 version := payload.NewVersion(0, 1337, "/NEO-GO/", capabilities) 328 329 require.NoError(t, s.handleVersionCmd(p, version)) 330 } 331 332 // Server should not reply with a verack after receiving a 333 // invalid version and disconnects the peer. 334 func TestServerNotSendsVerack(t *testing.T) { 335 var ( 336 s = newTestServer(t, ServerConfig{MaxPeers: 10, Net: 56753}) 337 p = newLocalPeer(t, s) 338 p2 = newLocalPeer(t, s) 339 ) 340 s.id = 1 341 startWithCleanup(t, s) 342 343 na, _ := net.ResolveTCPAddr("tcp", "0.0.0.0:3000") 344 p.netaddr = *na 345 p2.netaddr = *na 346 s.register <- p 347 348 capabilities := []capability.Capability{ 349 { 350 Type: capability.FullNode, 351 Data: &capability.Node{ 352 StartHeight: 0, 353 }, 354 }, 355 { 356 Type: capability.TCPServer, 357 Data: &capability.Server{ 358 Port: 3000, 359 }, 360 }, 361 } 362 // identical id's 363 version := payload.NewVersion(56753, 1, "/NEO-GO/", capabilities) 364 err := s.handleVersionCmd(p, version) 365 assert.NotNil(t, err) 366 assert.Equal(t, errIdenticalID, err) 367 368 // Different IDs, but also different magics 369 version.Nonce = 2 370 version.Magic = 56752 371 err = s.handleVersionCmd(p, version) 372 assert.NotNil(t, err) 373 assert.Equal(t, errInvalidNetwork, err) 374 375 // Different IDs and same network, make handshake pass. 376 version.Magic = 56753 377 require.NoError(t, s.handleVersionCmd(p, version)) 378 require.NoError(t, p.HandleVersionAck()) 379 require.Equal(t, true, p.Handshaked()) 380 381 // Second handshake from the same peer should fail. 382 s.register <- p2 383 err = s.handleVersionCmd(p2, version) 384 assert.NotNil(t, err) 385 require.Equal(t, errAlreadyConnected, err) 386 } 387 388 func (s *Server) testHandleMessage(t *testing.T, p Peer, cmd CommandType, pl payload.Payload) *Server { 389 if p == nil { 390 p = newLocalPeer(t, s) 391 p.(*localPeer).handshaked = 1 392 } 393 msg := NewMessage(cmd, pl) 394 require.NoError(t, s.handleMessage(p, msg)) 395 return s 396 } 397 398 func startTestServer(t *testing.T, protocolCfg ...func(*config.Blockchain)) *Server { 399 var s *Server 400 srvCfg := ServerConfig{UserAgent: "/test/"} 401 if protocolCfg != nil { 402 s = newTestServerWithCustomCfg(t, srvCfg, protocolCfg[0]) 403 } else { 404 s = newTestServer(t, srvCfg) 405 } 406 startWithCleanup(t, s) 407 return s 408 } 409 410 func startWithCleanup(t *testing.T, s *Server) { 411 s.Start() 412 t.Cleanup(func() { 413 s.Shutdown() 414 }) 415 } 416 417 func TestBlock(t *testing.T) { 418 s := startTestServer(t) 419 420 s.chain.(*fakechain.FakeChain).Blockheight.Store(12344) 421 require.Equal(t, uint32(12344), s.chain.BlockHeight()) 422 423 b := block.New(false) 424 b.Index = 12345 425 s.testHandleMessage(t, nil, CMDBlock, b) 426 require.Eventually(t, func() bool { return s.chain.BlockHeight() == 12345 }, 2*time.Second, time.Millisecond*500) 427 } 428 429 func TestConsensus(t *testing.T) { 430 s := newTestServer(t, ServerConfig{}) 431 cons := new(fakeConsensus) 432 s.AddConsensusService(cons, cons.OnPayload, cons.OnTransaction) 433 startWithCleanup(t, s) 434 435 s.chain.(*fakechain.FakeChain).Blockheight.Store(4) 436 p := newLocalPeer(t, s) 437 p.handshaked = 1 438 s.register <- p 439 require.Eventually(t, func() bool { return 1 == s.PeerCount() }, time.Second, time.Millisecond*10) 440 441 newConsensusMessage := func(start, end uint32) *Message { 442 pl := payload.NewExtensible() 443 pl.Category = payload.ConsensusCategory 444 pl.ValidBlockStart = start 445 pl.ValidBlockEnd = end 446 return NewMessage(CMDExtensible, pl) 447 } 448 449 s.chain.(*fakechain.FakeChain).VerifyWitnessF = func() (int64, error) { return 0, errors.New("invalid") } 450 msg := newConsensusMessage(0, s.chain.BlockHeight()+1) 451 require.Error(t, s.handleMessage(p, msg)) 452 453 s.chain.(*fakechain.FakeChain).VerifyWitnessF = func() (int64, error) { return 0, nil } 454 require.NoError(t, s.handleMessage(p, msg)) 455 require.Contains(t, s.services["fake"].(*fakeConsensus).payloads, msg.Payload.(*payload.Extensible)) 456 457 t.Run("small ValidUntilBlockEnd", func(t *testing.T) { 458 t.Run("current height", func(t *testing.T) { 459 msg := newConsensusMessage(0, s.chain.BlockHeight()) 460 require.NoError(t, s.handleMessage(p, msg)) 461 require.NotContains(t, s.services["fake"].(*fakeConsensus).payloads, msg.Payload.(*payload.Extensible)) 462 }) 463 t.Run("invalid", func(t *testing.T) { 464 msg := newConsensusMessage(0, s.chain.BlockHeight()-1) 465 require.Error(t, s.handleMessage(p, msg)) 466 }) 467 }) 468 t.Run("big ValidUntiLBlockStart", func(t *testing.T) { 469 msg := newConsensusMessage(s.chain.BlockHeight()+1, s.chain.BlockHeight()+2) 470 require.Error(t, s.handleMessage(p, msg)) 471 }) 472 } 473 474 func TestTransaction(t *testing.T) { 475 s := newTestServer(t, ServerConfig{}) 476 cons := new(fakeConsensus) 477 s.AddConsensusService(cons, cons.OnPayload, cons.OnTransaction) 478 startWithCleanup(t, s) 479 480 t.Run("good", func(t *testing.T) { 481 tx := newDummyTx() 482 s.RequestTx(tx.Hash()) 483 p := newLocalPeer(t, s) 484 p.isFullNode = true 485 p.messageHandler = func(t *testing.T, msg *Message) { 486 if msg.Command == CMDInv { 487 inv := msg.Payload.(*payload.Inventory) 488 require.Equal(t, payload.TXType, inv.Type) 489 require.Equal(t, []util.Uint256{tx.Hash()}, inv.Hashes) 490 } 491 } 492 s.register <- p 493 494 s.testHandleMessage(t, nil, CMDTX, tx) 495 require.Eventually(t, func() bool { 496 var fake = s.services["fake"].(*fakeConsensus) 497 fake.txlock.Lock() 498 defer fake.txlock.Unlock() 499 for _, t := range fake.txs { 500 if t == tx { 501 return true 502 } 503 } 504 return false 505 }, 2*time.Second, time.Millisecond*500) 506 }) 507 t.Run("bad", func(t *testing.T) { 508 tx := newDummyTx() 509 s.RequestTx(tx.Hash()) 510 s.chain.(*fakechain.FakeChain).PoolTxF = func(*transaction.Transaction) error { return core.ErrInsufficientFunds } 511 s.testHandleMessage(t, nil, CMDTX, tx) 512 require.Eventually(t, func() bool { 513 var fake = s.services["fake"].(*fakeConsensus) 514 fake.txlock.Lock() 515 defer fake.txlock.Unlock() 516 for _, t := range fake.txs { 517 if t == tx { 518 return true 519 } 520 } 521 return false 522 }, 2*time.Second, time.Millisecond*500) 523 }) 524 } 525 526 func (s *Server) testHandleGetData(t *testing.T, invType payload.InventoryType, hs, notFound []util.Uint256, found payload.Payload) { 527 var recvResponse atomic.Bool 528 var recvNotFound atomic.Bool 529 530 p := newLocalPeer(t, s) 531 p.handshaked = 1 532 p.messageHandler = func(t *testing.T, msg *Message) { 533 switch msg.Command { 534 case CMDTX, CMDBlock, CMDExtensible, CMDP2PNotaryRequest: 535 require.Equal(t, found, msg.Payload) 536 recvResponse.Store(true) 537 case CMDNotFound: 538 require.Equal(t, notFound, msg.Payload.(*payload.Inventory).Hashes) 539 recvNotFound.Store(true) 540 } 541 } 542 543 s.testHandleMessage(t, p, CMDGetData, payload.NewInventory(invType, hs)) 544 545 require.Eventually(t, func() bool { return recvResponse.Load() }, 2*time.Second, time.Millisecond) 546 require.Eventually(t, func() bool { return recvNotFound.Load() }, 2*time.Second, time.Millisecond) 547 } 548 549 func TestGetData(t *testing.T) { 550 s := startTestServer(t) 551 s.chain.(*fakechain.FakeChain).UtilityTokenBalance = big.NewInt(1000000) 552 553 t.Run("block", func(t *testing.T) { 554 b := newDummyBlock(2, 0) 555 hs := []util.Uint256{random.Uint256(), b.Hash(), random.Uint256()} 556 s.chain.(*fakechain.FakeChain).PutBlock(b) 557 notFound := []util.Uint256{hs[0], hs[2]} 558 s.testHandleGetData(t, payload.BlockType, hs, notFound, b) 559 }) 560 t.Run("transaction", func(t *testing.T) { 561 tx := newDummyTx() 562 hs := []util.Uint256{random.Uint256(), tx.Hash(), random.Uint256()} 563 s.chain.(*fakechain.FakeChain).PutTx(tx) 564 notFound := []util.Uint256{hs[0], hs[2]} 565 s.testHandleGetData(t, payload.TXType, hs, notFound, tx) 566 }) 567 t.Run("p2pNotaryRequest", func(t *testing.T) { 568 mainTx := &transaction.Transaction{ 569 Attributes: []transaction.Attribute{{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 1}}}, 570 Script: []byte{0, 1, 2}, 571 ValidUntilBlock: 123, 572 Signers: []transaction.Signer{{Account: random.Uint160()}}, 573 Scripts: []transaction.Witness{{InvocationScript: []byte{1, 2, 3}, VerificationScript: []byte{1, 2, 3}}}, 574 } 575 mainTx.Size() 576 mainTx.Hash() 577 fallbackTx := &transaction.Transaction{ 578 Script: []byte{1, 2, 3}, 579 ValidUntilBlock: 123, 580 Attributes: []transaction.Attribute{ 581 {Type: transaction.NotValidBeforeT, Value: &transaction.NotValidBefore{Height: 123}}, 582 {Type: transaction.ConflictsT, Value: &transaction.Conflicts{Hash: mainTx.Hash()}}, 583 {Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 0}}, 584 }, 585 Signers: []transaction.Signer{{Account: random.Uint160()}, {Account: random.Uint160()}}, 586 Scripts: []transaction.Witness{{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: make([]byte, 0)}, {InvocationScript: []byte{}, VerificationScript: []byte{}}}, 587 } 588 fallbackTx.Size() 589 fallbackTx.Hash() 590 r := &payload.P2PNotaryRequest{ 591 MainTransaction: mainTx, 592 FallbackTransaction: fallbackTx, 593 Witness: transaction.Witness{ 594 InvocationScript: []byte{1, 2, 3}, 595 VerificationScript: []byte{1, 2, 3}, 596 }, 597 } 598 r.Hash() 599 require.NoError(t, s.notaryRequestPool.Add(r.FallbackTransaction, s.chain, r)) 600 hs := []util.Uint256{random.Uint256(), r.FallbackTransaction.Hash(), random.Uint256()} 601 notFound := []util.Uint256{hs[0], hs[2]} 602 s.testHandleGetData(t, payload.P2PNotaryRequestType, hs, notFound, r) 603 }) 604 } 605 606 func initGetBlocksTest(t *testing.T) (*Server, []*block.Block) { 607 s := startTestServer(t) 608 609 var blocks []*block.Block 610 for i := uint32(12); i <= 15; i++ { 611 b := newDummyBlock(i, 3) 612 s.chain.(*fakechain.FakeChain).PutBlock(b) 613 blocks = append(blocks, b) 614 } 615 return s, blocks 616 } 617 618 func TestGetBlocks(t *testing.T) { 619 s, blocks := initGetBlocksTest(t) 620 621 expected := make([]util.Uint256, len(blocks)) 622 for i := range blocks { 623 expected[i] = blocks[i].Hash() 624 } 625 var actual []util.Uint256 626 p := newLocalPeer(t, s) 627 p.handshaked = 1 628 p.messageHandler = func(t *testing.T, msg *Message) { 629 if msg.Command == CMDInv { 630 actual = msg.Payload.(*payload.Inventory).Hashes 631 } 632 } 633 634 t.Run("2", func(t *testing.T) { 635 s.testHandleMessage(t, p, CMDGetBlocks, &payload.GetBlocks{HashStart: expected[0], Count: 2}) 636 require.Equal(t, expected[1:3], actual) 637 }) 638 t.Run("-1", func(t *testing.T) { 639 s.testHandleMessage(t, p, CMDGetBlocks, &payload.GetBlocks{HashStart: expected[0], Count: -1}) 640 require.Equal(t, expected[1:], actual) 641 }) 642 t.Run("invalid start", func(t *testing.T) { 643 msg := NewMessage(CMDGetBlocks, &payload.GetBlocks{HashStart: util.Uint256{}, Count: -1}) 644 require.Error(t, s.handleMessage(p, msg)) 645 }) 646 } 647 648 func TestGetBlockByIndex(t *testing.T) { 649 s, blocks := initGetBlocksTest(t) 650 651 var expected []*block.Block 652 var actual []*block.Block 653 p := newLocalPeer(t, s) 654 p.handshaked = 1 655 p.messageHandler = func(t *testing.T, msg *Message) { 656 if msg.Command == CMDBlock { 657 actual = append(actual, msg.Payload.(*block.Block)) 658 if len(actual) == len(expected) { 659 require.Equal(t, expected, actual) 660 } 661 } 662 } 663 664 t.Run("2", func(t *testing.T) { 665 actual = nil 666 expected = blocks[:2] 667 s.testHandleMessage(t, p, CMDGetBlockByIndex, &payload.GetBlockByIndex{IndexStart: blocks[0].Index, Count: 2}) 668 }) 669 t.Run("-1", func(t *testing.T) { 670 actual = nil 671 expected = blocks 672 s.testHandleMessage(t, p, CMDGetBlockByIndex, &payload.GetBlockByIndex{IndexStart: blocks[0].Index, Count: -1}) 673 }) 674 t.Run("-1, last header", func(t *testing.T) { 675 s.chain.(*fakechain.FakeChain).PutHeader(newDummyBlock(16, 2)) 676 actual = nil 677 expected = blocks 678 s.testHandleMessage(t, p, CMDGetBlockByIndex, &payload.GetBlockByIndex{IndexStart: blocks[0].Index, Count: -1}) 679 }) 680 } 681 682 func TestGetHeaders(t *testing.T) { 683 s, blocks := initGetBlocksTest(t) 684 685 expected := make([]*block.Header, len(blocks)) 686 for i := range blocks { 687 expected[i] = &blocks[i].Header 688 } 689 690 var actual *payload.Headers 691 p := newLocalPeer(t, s) 692 p.handshaked = 1 693 p.messageHandler = func(t *testing.T, msg *Message) { 694 if msg.Command == CMDHeaders { 695 actual = msg.Payload.(*payload.Headers) 696 } 697 } 698 699 t.Run("2", func(t *testing.T) { 700 actual = nil 701 s.testHandleMessage(t, p, CMDGetHeaders, &payload.GetBlockByIndex{IndexStart: blocks[0].Index, Count: 2}) 702 require.Equal(t, expected[:2], actual.Hdrs) 703 }) 704 t.Run("more, than we have", func(t *testing.T) { 705 actual = nil 706 s.testHandleMessage(t, p, CMDGetHeaders, &payload.GetBlockByIndex{IndexStart: blocks[0].Index, Count: 10}) 707 require.Equal(t, expected, actual.Hdrs) 708 }) 709 t.Run("-1", func(t *testing.T) { 710 actual = nil 711 s.testHandleMessage(t, p, CMDGetHeaders, &payload.GetBlockByIndex{IndexStart: blocks[0].Index, Count: -1}) 712 require.Equal(t, expected, actual.Hdrs) 713 }) 714 t.Run("no headers", func(t *testing.T) { 715 actual = nil 716 s.testHandleMessage(t, p, CMDGetHeaders, &payload.GetBlockByIndex{IndexStart: 123, Count: -1}) 717 require.Nil(t, actual) 718 }) 719 t.Run("distribute requests between peers", func(t *testing.T) { 720 testGetBlocksByIndex(t, CMDGetHeaders) 721 }) 722 } 723 724 func TestInv(t *testing.T) { 725 s := startTestServer(t) 726 s.chain.(*fakechain.FakeChain).UtilityTokenBalance = big.NewInt(10000000) 727 728 var actual []util.Uint256 729 p := newLocalPeer(t, s) 730 p.handshaked = 1 731 p.messageHandler = func(t *testing.T, msg *Message) { 732 if msg.Command == CMDGetData { 733 actual = msg.Payload.(*payload.Inventory).Hashes 734 } 735 } 736 737 t.Run("blocks", func(t *testing.T) { 738 b := newDummyBlock(10, 3) 739 s.chain.(*fakechain.FakeChain).PutBlock(b) 740 hs := []util.Uint256{random.Uint256(), b.Hash(), random.Uint256()} 741 s.testHandleMessage(t, p, CMDInv, &payload.Inventory{ 742 Type: payload.BlockType, 743 Hashes: hs, 744 }) 745 require.Equal(t, []util.Uint256{hs[0], hs[2]}, actual) 746 }) 747 t.Run("transaction", func(t *testing.T) { 748 tx := newDummyTx() 749 require.NoError(t, s.chain.GetMemPool().Add(tx, s.chain)) 750 hs := []util.Uint256{random.Uint256(), tx.Hash(), random.Uint256()} 751 s.testHandleMessage(t, p, CMDInv, &payload.Inventory{ 752 Type: payload.TXType, 753 Hashes: hs, 754 }) 755 require.Equal(t, []util.Uint256{hs[0], hs[2]}, actual) 756 }) 757 t.Run("extensible", func(t *testing.T) { 758 ep := payload.NewExtensible() 759 s.chain.(*fakechain.FakeChain).VerifyWitnessF = func() (int64, error) { return 0, nil } 760 ep.ValidBlockEnd = s.chain.(*fakechain.FakeChain).BlockHeight() + 1 761 ok, err := s.extensiblePool.Add(ep) 762 require.NoError(t, err) 763 require.True(t, ok) 764 s.testHandleMessage(t, p, CMDInv, &payload.Inventory{ 765 Type: payload.ExtensibleType, 766 Hashes: []util.Uint256{ep.Hash()}, 767 }) 768 }) 769 t.Run("p2pNotaryRequest", func(t *testing.T) { 770 fallbackTx := transaction.New(random.Bytes(100), 123) 771 fallbackTx.Signers = []transaction.Signer{{Account: random.Uint160()}, {Account: random.Uint160()}} 772 fallbackTx.Size() 773 fallbackTx.Hash() 774 r := &payload.P2PNotaryRequest{ 775 MainTransaction: newDummyTx(), 776 FallbackTransaction: fallbackTx, 777 } 778 require.NoError(t, s.notaryRequestPool.Add(r.FallbackTransaction, s.chain, r)) 779 hs := []util.Uint256{random.Uint256(), r.FallbackTransaction.Hash(), random.Uint256()} 780 s.testHandleMessage(t, p, CMDInv, &payload.Inventory{ 781 Type: payload.P2PNotaryRequestType, 782 Hashes: hs, 783 }) 784 require.Equal(t, []util.Uint256{hs[0], hs[2]}, actual) 785 }) 786 } 787 788 func TestHandleGetMPTData(t *testing.T) { 789 t.Run("P2PStateExchange extensions off", func(t *testing.T) { 790 s := startTestServer(t) 791 p := newLocalPeer(t, s) 792 p.handshaked = 1 793 msg := NewMessage(CMDGetMPTData, &payload.MPTInventory{ 794 Hashes: []util.Uint256{{1, 2, 3}}, 795 }) 796 require.Error(t, s.handleMessage(p, msg)) 797 }) 798 799 check := func(t *testing.T, s *Server) { 800 var recvResponse atomic.Bool 801 r1 := random.Uint256() 802 r2 := random.Uint256() 803 r3 := random.Uint256() 804 node := []byte{1, 2, 3} 805 s.stateSync.(*fakechain.FakeStateSync).TraverseFunc = func(root util.Uint256, process func(node mpt.Node, nodeBytes []byte) bool) error { 806 if !(root.Equals(r1) || root.Equals(r2)) { 807 t.Fatal("unexpected root") 808 } 809 require.False(t, process(mpt.NewHashNode(r3), node)) 810 return nil 811 } 812 found := &payload.MPTData{ 813 Nodes: [][]byte{node}, // no duplicates expected 814 } 815 p := newLocalPeer(t, s) 816 p.handshaked = 1 817 p.messageHandler = func(t *testing.T, msg *Message) { 818 switch msg.Command { 819 case CMDMPTData: 820 require.Equal(t, found, msg.Payload) 821 recvResponse.Store(true) 822 } 823 } 824 hs := []util.Uint256{r1, r2} 825 s.testHandleMessage(t, p, CMDGetMPTData, payload.NewMPTInventory(hs)) 826 827 require.Eventually(t, recvResponse.Load, time.Second, time.Millisecond) 828 } 829 t.Run("KeepOnlyLatestState on", func(t *testing.T) { 830 s := startTestServer(t, func(c *config.Blockchain) { 831 c.P2PStateExchangeExtensions = true 832 c.Ledger.KeepOnlyLatestState = true 833 }) 834 check(t, s) 835 }) 836 837 t.Run("good", func(t *testing.T) { 838 s := startTestServer(t, func(c *config.Blockchain) { 839 c.P2PStateExchangeExtensions = true 840 }) 841 check(t, s) 842 }) 843 } 844 845 func TestHandleMPTData(t *testing.T) { 846 t.Run("P2PStateExchange extensions off", func(t *testing.T) { 847 s := startTestServer(t) 848 p := newLocalPeer(t, s) 849 p.handshaked = 1 850 msg := NewMessage(CMDMPTData, &payload.MPTData{ 851 Nodes: [][]byte{{1, 2, 3}}, 852 }) 853 require.Error(t, s.handleMessage(p, msg)) 854 }) 855 856 t.Run("good", func(t *testing.T) { 857 expected := [][]byte{{1, 2, 3}, {2, 3, 4}} 858 s := newTestServer(t, ServerConfig{UserAgent: "/test/"}) 859 s.config.P2PStateExchangeExtensions = true 860 s.stateSync = &fakechain.FakeStateSync{ 861 AddMPTNodesFunc: func(nodes [][]byte) error { 862 require.Equal(t, expected, nodes) 863 return nil 864 }, 865 } 866 startWithCleanup(t, s) 867 868 p := newLocalPeer(t, s) 869 p.handshaked = 1 870 msg := NewMessage(CMDMPTData, &payload.MPTData{ 871 Nodes: expected, 872 }) 873 require.NoError(t, s.handleMessage(p, msg)) 874 }) 875 } 876 877 func TestRequestMPTNodes(t *testing.T) { 878 s := startTestServer(t) 879 880 var actual []util.Uint256 881 p := newLocalPeer(t, s) 882 p.handshaked = 1 883 p.messageHandler = func(t *testing.T, msg *Message) { 884 if msg.Command == CMDGetMPTData { 885 actual = append(actual, msg.Payload.(*payload.MPTInventory).Hashes...) 886 } 887 } 888 s.register <- p 889 s.register <- p // ensure previous send was handled 890 891 t.Run("no hashes, no message", func(t *testing.T) { 892 actual = nil 893 require.NoError(t, s.requestMPTNodes(p, nil)) 894 require.Nil(t, actual) 895 }) 896 t.Run("good, small", func(t *testing.T) { 897 actual = nil 898 expected := []util.Uint256{random.Uint256(), random.Uint256()} 899 require.NoError(t, s.requestMPTNodes(p, expected)) 900 require.Equal(t, expected, actual) 901 }) 902 t.Run("good, exactly one chunk", func(t *testing.T) { 903 actual = nil 904 expected := make([]util.Uint256, payload.MaxMPTHashesCount) 905 for i := range expected { 906 expected[i] = random.Uint256() 907 } 908 require.NoError(t, s.requestMPTNodes(p, expected)) 909 require.Equal(t, expected, actual) 910 }) 911 t.Run("good, too large chunk", func(t *testing.T) { 912 actual = nil 913 expected := make([]util.Uint256, payload.MaxMPTHashesCount+1) 914 for i := range expected { 915 expected[i] = random.Uint256() 916 } 917 require.NoError(t, s.requestMPTNodes(p, expected)) 918 require.Equal(t, expected[:payload.MaxMPTHashesCount], actual) 919 }) 920 } 921 922 func TestRequestTx(t *testing.T) { 923 s := startTestServer(t) 924 925 var actual []util.Uint256 926 p := newLocalPeer(t, s) 927 p.handshaked = 1 928 p.messageHandler = func(t *testing.T, msg *Message) { 929 if msg.Command == CMDGetData { 930 actual = append(actual, msg.Payload.(*payload.Inventory).Hashes...) 931 } 932 } 933 s.register <- p 934 s.register <- p // ensure previous send was handled 935 936 t.Run("no hashes, no message", func(t *testing.T) { 937 actual = nil 938 s.RequestTx() 939 require.Nil(t, actual) 940 }) 941 t.Run("good, small", func(t *testing.T) { 942 actual = nil 943 expected := []util.Uint256{random.Uint256(), random.Uint256()} 944 s.RequestTx(expected...) 945 require.Equal(t, expected, actual) 946 }) 947 t.Run("good, exactly one chunk", func(t *testing.T) { 948 actual = nil 949 expected := make([]util.Uint256, payload.MaxHashesCount) 950 for i := range expected { 951 expected[i] = random.Uint256() 952 } 953 s.RequestTx(expected...) 954 require.Equal(t, expected, actual) 955 }) 956 t.Run("good, multiple chunks", func(t *testing.T) { 957 actual = nil 958 expected := make([]util.Uint256, payload.MaxHashesCount*2+payload.MaxHashesCount/2) 959 for i := range expected { 960 expected[i] = random.Uint256() 961 } 962 s.RequestTx(expected...) 963 require.Equal(t, expected, actual) 964 }) 965 } 966 967 func TestAddrs(t *testing.T) { 968 s := startTestServer(t) 969 970 ips := make([][16]byte, 4) 971 copy(ips[0][:], net.IPv4(1, 2, 3, 4)) 972 copy(ips[1][:], net.IPv4(7, 8, 9, 0)) 973 for i := range ips[2] { 974 ips[2][i] = byte(i) 975 } 976 977 p := newLocalPeer(t, s) 978 p.handshaked = 1 979 p.getAddrSent = 1 980 pl := &payload.AddressList{ 981 Addrs: []*payload.AddressAndTime{ 982 { 983 IP: ips[0], 984 Capabilities: capability.Capabilities{{ 985 Type: capability.TCPServer, 986 Data: &capability.Server{Port: 12}, 987 }}, 988 }, 989 { 990 IP: ips[1], 991 Capabilities: capability.Capabilities{}, 992 }, 993 { 994 IP: ips[2], 995 Capabilities: capability.Capabilities{{ 996 Type: capability.TCPServer, 997 Data: &capability.Server{Port: 42}, 998 }}, 999 }, 1000 }, 1001 } 1002 s.testHandleMessage(t, p, CMDAddr, pl) 1003 1004 addrs := s.discovery.(*testDiscovery).backfill 1005 require.Equal(t, 2, len(addrs)) 1006 require.Equal(t, "1.2.3.4:12", addrs[0]) 1007 require.Equal(t, net.JoinHostPort(net.IP(ips[2][:]).String(), "42"), addrs[1]) 1008 1009 t.Run("CMDAddr not requested", func(t *testing.T) { 1010 msg := NewMessage(CMDAddr, pl) 1011 require.Error(t, s.handleMessage(p, msg)) 1012 }) 1013 } 1014 1015 type feerStub struct { 1016 blockHeight uint32 1017 } 1018 1019 func (f feerStub) FeePerByte() int64 { return 1 } 1020 func (f feerStub) GetUtilityTokenBalance(util.Uint160) *big.Int { return big.NewInt(100000000) } 1021 func (f feerStub) BlockHeight() uint32 { return f.blockHeight } 1022 func (f feerStub) GetBaseExecFee() int64 { return interop.DefaultBaseExecFee } 1023 1024 func TestMemPool(t *testing.T) { 1025 s := startTestServer(t) 1026 1027 var actual []util.Uint256 1028 p := newLocalPeer(t, s) 1029 p.handshaked = 1 1030 p.messageHandler = func(t *testing.T, msg *Message) { 1031 if msg.Command == CMDInv { 1032 actual = append(actual, msg.Payload.(*payload.Inventory).Hashes...) 1033 } 1034 } 1035 1036 bc := s.chain.(*fakechain.FakeChain) 1037 expected := make([]util.Uint256, 4) 1038 for i := range expected { 1039 tx := newDummyTx() 1040 require.NoError(t, bc.Pool.Add(tx, &feerStub{blockHeight: 10})) 1041 expected[i] = tx.Hash() 1042 } 1043 1044 s.testHandleMessage(t, p, CMDMempool, payload.NullPayload{}) 1045 require.ElementsMatch(t, expected, actual) 1046 } 1047 1048 func TestVerifyNotaryRequest(t *testing.T) { 1049 bc := fakechain.NewFakeChain() 1050 bc.MaxVerificationGAS = 10 1051 bc.NotaryContractScriptHash = util.Uint160{1, 2, 3} 1052 s, err := newServerFromConstructors(ServerConfig{Addresses: []config.AnnounceableAddress{{Address: ":0"}}}, bc, new(fakechain.FakeStateSync), zaptest.NewLogger(t), newFakeTransp, newTestDiscovery) 1053 require.NoError(t, err) 1054 newNotaryRequest := func() *payload.P2PNotaryRequest { 1055 return &payload.P2PNotaryRequest{ 1056 MainTransaction: &transaction.Transaction{ 1057 Script: []byte{0, 1, 2}, 1058 Signers: []transaction.Signer{{Account: random.Uint160()}}, 1059 }, 1060 FallbackTransaction: &transaction.Transaction{ 1061 ValidUntilBlock: 321, 1062 Signers: []transaction.Signer{{Account: bc.NotaryContractScriptHash}, {Account: random.Uint160()}}, 1063 }, 1064 Witness: transaction.Witness{}, 1065 } 1066 } 1067 1068 t.Run("bad payload witness", func(t *testing.T) { 1069 bc.VerifyWitnessF = func() (int64, error) { return 0, errors.New("bad witness") } 1070 require.Error(t, s.verifyNotaryRequest(nil, newNotaryRequest())) 1071 }) 1072 1073 t.Run("bad fallback sender", func(t *testing.T) { 1074 bc.VerifyWitnessF = func() (int64, error) { return 0, nil } 1075 r := newNotaryRequest() 1076 r.FallbackTransaction.Signers[0] = transaction.Signer{Account: util.Uint160{7, 8, 9}} 1077 require.Error(t, s.verifyNotaryRequest(nil, r)) 1078 }) 1079 1080 t.Run("bad main sender", func(t *testing.T) { 1081 bc.VerifyWitnessF = func() (int64, error) { return 0, nil } 1082 r := newNotaryRequest() 1083 r.MainTransaction.Signers[0] = transaction.Signer{Account: bc.NotaryContractScriptHash} 1084 require.Error(t, s.verifyNotaryRequest(nil, r)) 1085 }) 1086 1087 t.Run("expired deposit", func(t *testing.T) { 1088 r := newNotaryRequest() 1089 bc.NotaryDepositExpiration = r.FallbackTransaction.ValidUntilBlock 1090 require.Error(t, s.verifyNotaryRequest(nil, r)) 1091 }) 1092 1093 t.Run("good", func(t *testing.T) { 1094 r := newNotaryRequest() 1095 bc.NotaryDepositExpiration = r.FallbackTransaction.ValidUntilBlock + 1 1096 require.NoError(t, s.verifyNotaryRequest(nil, r)) 1097 }) 1098 } 1099 1100 func TestTryInitStateSync(t *testing.T) { 1101 t.Run("module inactive", func(t *testing.T) { 1102 s := startTestServer(t) 1103 s.tryInitStateSync() 1104 }) 1105 1106 t.Run("module already initialized", func(t *testing.T) { 1107 s := startTestServer(t) 1108 ss := &fakechain.FakeStateSync{} 1109 ss.IsActiveFlag.Store(true) 1110 ss.IsInitializedFlag.Store(true) 1111 s.stateSync = ss 1112 s.tryInitStateSync() 1113 }) 1114 1115 t.Run("good", func(t *testing.T) { 1116 s := startTestServer(t) 1117 for _, h := range []uint32{10, 8, 7, 4, 11, 4} { 1118 p := newLocalPeer(t, s) 1119 p.handshaked = 1 1120 p.lastBlockIndex = h 1121 s.register <- p 1122 } 1123 p := newLocalPeer(t, s) 1124 p.handshaked = 0 // one disconnected peer to check it won't be taken into attention 1125 p.lastBlockIndex = 5 1126 s.register <- p 1127 require.Eventually(t, func() bool { return 7 == s.PeerCount() }, time.Second, time.Millisecond*10) 1128 1129 var expectedH uint32 = 8 // median peer 1130 ss := &fakechain.FakeStateSync{InitFunc: func(h uint32) error { 1131 if h != expectedH { 1132 return fmt.Errorf("invalid height: expected %d, got %d", expectedH, h) 1133 } 1134 return nil 1135 }} 1136 ss.IsActiveFlag.Store(true) 1137 s.stateSync = ss 1138 s.tryInitStateSync() 1139 }) 1140 } 1141 1142 func TestServer_Port(t *testing.T) { 1143 s := newTestServer(t, ServerConfig{ 1144 Addresses: []config.AnnounceableAddress{ 1145 {Address: "1.2.3.4:10"}, // some random address 1146 {Address: ":1"}, // listen all IPs 1147 {Address: "127.0.0.1:2"}, // address without announced port 1148 {Address: "123.123.0.123:3", AnnouncedPort: 123}}, // address with announced port 1149 }) 1150 1151 // Default addr => first port available 1152 actual, err := s.Port(nil) 1153 require.NoError(t, err) 1154 require.Equal(t, uint16(10), actual) 1155 1156 // Specified address with direct match => port of matched address 1157 actual, err = s.Port(&net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 123}) 1158 require.NoError(t, err) 1159 require.Equal(t, uint16(2), actual) 1160 1161 // No address match => 0.0.0.0's port 1162 actual, err = s.Port(&net.TCPAddr{IP: net.IPv4(5, 6, 7, 8), Port: 123}) 1163 require.NoError(t, err) 1164 require.Equal(t, uint16(1), actual) 1165 1166 // Specified address with match on announceable address => announced port 1167 actual, err = s.Port(&net.TCPAddr{IP: net.IPv4(123, 123, 0, 123), Port: 123}) 1168 require.NoError(t, err) 1169 require.Equal(t, uint16(123), actual) 1170 }