Merge pull request #20140 from karalabe/eth64-handshake-forkid
eth: eth/64 - extend handshake with with fork id
This commit is contained in:
		| @@ -50,6 +50,9 @@ type ID struct { | ||||
| 	Next uint64  // Block number of the next upcoming fork, or 0 if no forks are known | ||||
| } | ||||
|  | ||||
| // Filter is a fork id filter to validate a remotely advertised ID. | ||||
| type Filter func(id ID) error | ||||
|  | ||||
| // NewID calculates the Ethereum fork ID from the chain config and head. | ||||
| func NewID(chain *core.BlockChain) ID { | ||||
| 	return newID( | ||||
| @@ -82,7 +85,7 @@ func newID(config *params.ChainConfig, genesis common.Hash, head uint64) ID { | ||||
|  | ||||
| // NewFilter creates a filter that returns if a fork ID should be rejected or not | ||||
| // based on the local chain's status. | ||||
| func NewFilter(chain *core.BlockChain) func(id ID) error { | ||||
| func NewFilter(chain *core.BlockChain) Filter { | ||||
| 	return newFilter( | ||||
| 		chain.Config(), | ||||
| 		chain.Genesis().Hash(), | ||||
|   | ||||
| @@ -29,6 +29,7 @@ import ( | ||||
| 	"github.com/ethereum/go-ethereum/common" | ||||
| 	"github.com/ethereum/go-ethereum/consensus" | ||||
| 	"github.com/ethereum/go-ethereum/core" | ||||
| 	"github.com/ethereum/go-ethereum/core/forkid" | ||||
| 	"github.com/ethereum/go-ethereum/core/types" | ||||
| 	"github.com/ethereum/go-ethereum/eth/downloader" | ||||
| 	"github.com/ethereum/go-ethereum/eth/fetcher" | ||||
| @@ -63,7 +64,8 @@ func errResp(code errCode, format string, v ...interface{}) error { | ||||
| } | ||||
|  | ||||
| type ProtocolManager struct { | ||||
| 	networkID uint64 | ||||
| 	networkID  uint64 | ||||
| 	forkFilter forkid.Filter // Fork ID filter, constant across the lifetime of the node | ||||
|  | ||||
| 	fastSync  uint32 // Flag whether fast sync is enabled (gets disabled if we already have blocks) | ||||
| 	acceptTxs uint32 // Flag whether we're considered synchronised (enables transaction processing) | ||||
| @@ -103,6 +105,7 @@ func NewProtocolManager(config *params.ChainConfig, checkpoint *params.TrustedCh | ||||
| 	// Create the protocol manager with the base fields | ||||
| 	manager := &ProtocolManager{ | ||||
| 		networkID:   networkID, | ||||
| 		forkFilter:  forkid.NewFilter(blockchain), | ||||
| 		eventMux:    mux, | ||||
| 		txpool:      txpool, | ||||
| 		blockchain:  blockchain, | ||||
| @@ -304,7 +307,7 @@ func (pm *ProtocolManager) handle(p *peer) error { | ||||
| 		number  = head.Number.Uint64() | ||||
| 		td      = pm.blockchain.GetTd(hash, number) | ||||
| 	) | ||||
| 	if err := p.Handshake(pm.networkID, td, hash, genesis.Hash()); err != nil { | ||||
| 	if err := p.Handshake(pm.networkID, td, hash, genesis.Hash(), forkid.NewID(pm.blockchain), pm.forkFilter); err != nil { | ||||
| 		p.Log().Debug("Ethereum handshake failed", "err", err) | ||||
| 		return err | ||||
| 	} | ||||
|   | ||||
| @@ -39,8 +39,8 @@ import ( | ||||
| ) | ||||
|  | ||||
| // Tests that block headers can be retrieved from a remote chain based on user queries. | ||||
| func TestGetBlockHeaders62(t *testing.T) { testGetBlockHeaders(t, 62) } | ||||
| func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) } | ||||
| func TestGetBlockHeaders64(t *testing.T) { testGetBlockHeaders(t, 64) } | ||||
|  | ||||
| func testGetBlockHeaders(t *testing.T, protocol int) { | ||||
| 	pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxHashFetch+15, nil, nil) | ||||
| @@ -198,8 +198,8 @@ func testGetBlockHeaders(t *testing.T, protocol int) { | ||||
| } | ||||
|  | ||||
| // Tests that block contents can be retrieved from a remote chain based on their hashes. | ||||
| func TestGetBlockBodies62(t *testing.T) { testGetBlockBodies(t, 62) } | ||||
| func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) } | ||||
| func TestGetBlockBodies64(t *testing.T) { testGetBlockBodies(t, 64) } | ||||
|  | ||||
| func testGetBlockBodies(t *testing.T, protocol int) { | ||||
| 	pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxBlockFetch+15, nil, nil) | ||||
| @@ -271,6 +271,7 @@ func testGetBlockBodies(t *testing.T, protocol int) { | ||||
|  | ||||
| // Tests that the node state database can be retrieved based on hashes. | ||||
| func TestGetNodeData63(t *testing.T) { testGetNodeData(t, 63) } | ||||
| func TestGetNodeData64(t *testing.T) { testGetNodeData(t, 64) } | ||||
|  | ||||
| func testGetNodeData(t *testing.T, protocol int) { | ||||
| 	// Define three accounts to simulate transactions with | ||||
| @@ -367,6 +368,7 @@ func testGetNodeData(t *testing.T, protocol int) { | ||||
|  | ||||
| // Tests that the transaction receipts can be retrieved based on hashes. | ||||
| func TestGetReceipt63(t *testing.T) { testGetReceipt(t, 63) } | ||||
| func TestGetReceipt64(t *testing.T) { testGetReceipt(t, 64) } | ||||
|  | ||||
| func testGetReceipt(t *testing.T, protocol int) { | ||||
| 	// Define three accounts to simulate transactions with | ||||
|   | ||||
| @@ -22,6 +22,7 @@ package eth | ||||
| import ( | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/rand" | ||||
| 	"fmt" | ||||
| 	"math/big" | ||||
| 	"sort" | ||||
| 	"sync" | ||||
| @@ -30,6 +31,7 @@ import ( | ||||
| 	"github.com/ethereum/go-ethereum/common" | ||||
| 	"github.com/ethereum/go-ethereum/consensus/ethash" | ||||
| 	"github.com/ethereum/go-ethereum/core" | ||||
| 	"github.com/ethereum/go-ethereum/core/forkid" | ||||
| 	"github.com/ethereum/go-ethereum/core/rawdb" | ||||
| 	"github.com/ethereum/go-ethereum/core/types" | ||||
| 	"github.com/ethereum/go-ethereum/core/vm" | ||||
| @@ -171,20 +173,35 @@ func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*te | ||||
| 			head    = pm.blockchain.CurrentHeader() | ||||
| 			td      = pm.blockchain.GetTd(head.Hash(), head.Number.Uint64()) | ||||
| 		) | ||||
| 		tp.handshake(nil, td, head.Hash(), genesis.Hash()) | ||||
| 		tp.handshake(nil, td, head.Hash(), genesis.Hash(), forkid.NewID(pm.blockchain), forkid.NewFilter(pm.blockchain)) | ||||
| 	} | ||||
| 	return tp, errc | ||||
| } | ||||
|  | ||||
| // handshake simulates a trivial handshake that expects the same state from the | ||||
| // remote side as we are simulating locally. | ||||
| func (p *testPeer) handshake(t *testing.T, td *big.Int, head common.Hash, genesis common.Hash) { | ||||
| 	msg := &statusData{ | ||||
| 		ProtocolVersion: uint32(p.version), | ||||
| 		NetworkId:       DefaultConfig.NetworkId, | ||||
| 		TD:              td, | ||||
| 		CurrentBlock:    head, | ||||
| 		GenesisBlock:    genesis, | ||||
| func (p *testPeer) handshake(t *testing.T, td *big.Int, head common.Hash, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter) { | ||||
| 	var msg interface{} | ||||
| 	switch { | ||||
| 	case p.version == eth63: | ||||
| 		msg = &statusData63{ | ||||
| 			ProtocolVersion: uint32(p.version), | ||||
| 			NetworkId:       DefaultConfig.NetworkId, | ||||
| 			TD:              td, | ||||
| 			CurrentBlock:    head, | ||||
| 			GenesisBlock:    genesis, | ||||
| 		} | ||||
| 	case p.version == eth64: | ||||
| 		msg = &statusData{ | ||||
| 			ProtocolVersion: uint32(p.version), | ||||
| 			NetworkID:       DefaultConfig.NetworkId, | ||||
| 			TD:              td, | ||||
| 			Head:            head, | ||||
| 			Genesis:         genesis, | ||||
| 			ForkID:          forkID, | ||||
| 		} | ||||
| 	default: | ||||
| 		panic(fmt.Sprintf("unsupported eth protocol version: %d", p.version)) | ||||
| 	} | ||||
| 	if err := p2p.ExpectMsg(p.app, StatusMsg, msg); err != nil { | ||||
| 		t.Fatalf("status recv: %v", err) | ||||
|   | ||||
							
								
								
									
										90
									
								
								eth/peer.go
									
									
									
									
									
								
							
							
						
						
									
										90
									
								
								eth/peer.go
									
									
									
									
									
								
							| @@ -25,6 +25,7 @@ import ( | ||||
|  | ||||
| 	mapset "github.com/deckarep/golang-set" | ||||
| 	"github.com/ethereum/go-ethereum/common" | ||||
| 	"github.com/ethereum/go-ethereum/core/forkid" | ||||
| 	"github.com/ethereum/go-ethereum/core/types" | ||||
| 	"github.com/ethereum/go-ethereum/p2p" | ||||
| 	"github.com/ethereum/go-ethereum/rlp" | ||||
| @@ -353,22 +354,46 @@ func (p *peer) RequestReceipts(hashes []common.Hash) error { | ||||
|  | ||||
| // Handshake executes the eth protocol handshake, negotiating version number, | ||||
| // network IDs, difficulties, head and genesis blocks. | ||||
| func (p *peer) Handshake(network uint64, td *big.Int, head common.Hash, genesis common.Hash) error { | ||||
| func (p *peer) Handshake(network uint64, td *big.Int, head common.Hash, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter) error { | ||||
| 	// Send out own handshake in a new thread | ||||
| 	errc := make(chan error, 2) | ||||
| 	var status statusData // safe to read after two values have been received from errc | ||||
|  | ||||
| 	var ( | ||||
| 		status63 statusData63 // safe to read after two values have been received from errc | ||||
| 		status   statusData   // safe to read after two values have been received from errc | ||||
| 	) | ||||
| 	go func() { | ||||
| 		errc <- p2p.Send(p.rw, StatusMsg, &statusData{ | ||||
| 			ProtocolVersion: uint32(p.version), | ||||
| 			NetworkId:       network, | ||||
| 			TD:              td, | ||||
| 			CurrentBlock:    head, | ||||
| 			GenesisBlock:    genesis, | ||||
| 		}) | ||||
| 		switch { | ||||
| 		case p.version == eth63: | ||||
| 			errc <- p2p.Send(p.rw, StatusMsg, &statusData63{ | ||||
| 				ProtocolVersion: uint32(p.version), | ||||
| 				NetworkId:       network, | ||||
| 				TD:              td, | ||||
| 				CurrentBlock:    head, | ||||
| 				GenesisBlock:    genesis, | ||||
| 			}) | ||||
| 		case p.version == eth64: | ||||
| 			errc <- p2p.Send(p.rw, StatusMsg, &statusData{ | ||||
| 				ProtocolVersion: uint32(p.version), | ||||
| 				NetworkID:       network, | ||||
| 				TD:              td, | ||||
| 				Head:            head, | ||||
| 				Genesis:         genesis, | ||||
| 				ForkID:          forkID, | ||||
| 			}) | ||||
| 		default: | ||||
| 			panic(fmt.Sprintf("unsupported eth protocol version: %d", p.version)) | ||||
| 		} | ||||
| 	}() | ||||
| 	go func() { | ||||
| 		errc <- p.readStatus(network, &status, genesis) | ||||
| 		switch { | ||||
| 		case p.version == eth63: | ||||
| 			errc <- p.readStatusLegacy(network, &status63, genesis) | ||||
| 		case p.version == eth64: | ||||
| 			errc <- p.readStatus(network, &status, genesis, forkFilter) | ||||
| 		default: | ||||
| 			panic(fmt.Sprintf("unsupported eth protocol version: %d", p.version)) | ||||
| 		} | ||||
| 	}() | ||||
| 	timeout := time.NewTimer(handshakeTimeout) | ||||
| 	defer timeout.Stop() | ||||
| @@ -382,11 +407,18 @@ func (p *peer) Handshake(network uint64, td *big.Int, head common.Hash, genesis | ||||
| 			return p2p.DiscReadTimeout | ||||
| 		} | ||||
| 	} | ||||
| 	p.td, p.head = status.TD, status.CurrentBlock | ||||
| 	switch { | ||||
| 	case p.version == eth63: | ||||
| 		p.td, p.head = status63.TD, status63.CurrentBlock | ||||
| 	case p.version == eth64: | ||||
| 		p.td, p.head = status.TD, status.Head | ||||
| 	default: | ||||
| 		panic(fmt.Sprintf("unsupported eth protocol version: %d", p.version)) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (p *peer) readStatus(network uint64, status *statusData, genesis common.Hash) (err error) { | ||||
| func (p *peer) readStatusLegacy(network uint64, status *statusData63, genesis common.Hash) error { | ||||
| 	msg, err := p.rw.ReadMsg() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| @@ -402,10 +434,10 @@ func (p *peer) readStatus(network uint64, status *statusData, genesis common.Has | ||||
| 		return errResp(ErrDecode, "msg %v: %v", msg, err) | ||||
| 	} | ||||
| 	if status.GenesisBlock != genesis { | ||||
| 		return errResp(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock[:8], genesis[:8]) | ||||
| 		return errResp(ErrGenesisMismatch, "%x (!= %x)", status.GenesisBlock[:8], genesis[:8]) | ||||
| 	} | ||||
| 	if status.NetworkId != network { | ||||
| 		return errResp(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, network) | ||||
| 		return errResp(ErrNetworkIDMismatch, "%d (!= %d)", status.NetworkId, network) | ||||
| 	} | ||||
| 	if int(status.ProtocolVersion) != p.version { | ||||
| 		return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, p.version) | ||||
| @@ -413,6 +445,36 @@ func (p *peer) readStatus(network uint64, status *statusData, genesis common.Has | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (p *peer) readStatus(network uint64, status *statusData, genesis common.Hash, forkFilter forkid.Filter) error { | ||||
| 	msg, err := p.rw.ReadMsg() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if msg.Code != StatusMsg { | ||||
| 		return errResp(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg) | ||||
| 	} | ||||
| 	if msg.Size > protocolMaxMsgSize { | ||||
| 		return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, protocolMaxMsgSize) | ||||
| 	} | ||||
| 	// Decode the handshake and make sure everything matches | ||||
| 	if err := msg.Decode(&status); err != nil { | ||||
| 		return errResp(ErrDecode, "msg %v: %v", msg, err) | ||||
| 	} | ||||
| 	if status.NetworkID != network { | ||||
| 		return errResp(ErrNetworkIDMismatch, "%d (!= %d)", status.NetworkID, network) | ||||
| 	} | ||||
| 	if int(status.ProtocolVersion) != p.version { | ||||
| 		return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, p.version) | ||||
| 	} | ||||
| 	if status.Genesis != genesis { | ||||
| 		return errResp(ErrGenesisMismatch, "%x (!= %x)", status.Genesis, genesis) | ||||
| 	} | ||||
| 	if err := forkFilter(status.ForkID); err != nil { | ||||
| 		return errResp(ErrForkIDRejected, "%v", err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // String implements fmt.Stringer. | ||||
| func (p *peer) String() string { | ||||
| 	return fmt.Sprintf("Peer %s [%s]", p.id, | ||||
|   | ||||
| @@ -23,6 +23,7 @@ import ( | ||||
|  | ||||
| 	"github.com/ethereum/go-ethereum/common" | ||||
| 	"github.com/ethereum/go-ethereum/core" | ||||
| 	"github.com/ethereum/go-ethereum/core/forkid" | ||||
| 	"github.com/ethereum/go-ethereum/core/types" | ||||
| 	"github.com/ethereum/go-ethereum/event" | ||||
| 	"github.com/ethereum/go-ethereum/rlp" | ||||
| @@ -30,24 +31,23 @@ import ( | ||||
|  | ||||
| // Constants to match up protocol versions and messages | ||||
| const ( | ||||
| 	eth62 = 62 | ||||
| 	eth63 = 63 | ||||
| 	eth64 = 64 | ||||
| ) | ||||
|  | ||||
| // protocolName is the official short name of the protocol used during capability negotiation. | ||||
| const protocolName = "eth" | ||||
|  | ||||
| // ProtocolVersions are the supported versions of the eth protocol (first is primary). | ||||
| var ProtocolVersions = []uint{eth63} | ||||
| var ProtocolVersions = []uint{eth64, eth63} | ||||
|  | ||||
| // protocolLengths are the number of implemented message corresponding to different protocol versions. | ||||
| var protocolLengths = map[uint]uint64{eth63: 17, eth62: 8} | ||||
| var protocolLengths = map[uint]uint64{eth64: 17, eth63: 17} | ||||
|  | ||||
| const protocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message | ||||
|  | ||||
| // eth protocol message codes | ||||
| const ( | ||||
| 	// Protocol messages belonging to eth/62 | ||||
| 	StatusMsg          = 0x00 | ||||
| 	NewBlockHashesMsg  = 0x01 | ||||
| 	TxMsg              = 0x02 | ||||
| @@ -56,12 +56,10 @@ const ( | ||||
| 	GetBlockBodiesMsg  = 0x05 | ||||
| 	BlockBodiesMsg     = 0x06 | ||||
| 	NewBlockMsg        = 0x07 | ||||
|  | ||||
| 	// Protocol messages belonging to eth/63 | ||||
| 	GetNodeDataMsg = 0x0d | ||||
| 	NodeDataMsg    = 0x0e | ||||
| 	GetReceiptsMsg = 0x0f | ||||
| 	ReceiptsMsg    = 0x10 | ||||
| 	GetNodeDataMsg     = 0x0d | ||||
| 	NodeDataMsg        = 0x0e | ||||
| 	GetReceiptsMsg     = 0x0f | ||||
| 	ReceiptsMsg        = 0x10 | ||||
| ) | ||||
|  | ||||
| type errCode int | ||||
| @@ -71,11 +69,11 @@ const ( | ||||
| 	ErrDecode | ||||
| 	ErrInvalidMsgCode | ||||
| 	ErrProtocolVersionMismatch | ||||
| 	ErrNetworkIdMismatch | ||||
| 	ErrGenesisBlockMismatch | ||||
| 	ErrNetworkIDMismatch | ||||
| 	ErrGenesisMismatch | ||||
| 	ErrForkIDRejected | ||||
| 	ErrNoStatusMsg | ||||
| 	ErrExtraStatusMsg | ||||
| 	ErrSuspendedPeer | ||||
| ) | ||||
|  | ||||
| func (e errCode) String() string { | ||||
| @@ -88,11 +86,11 @@ var errorToString = map[int]string{ | ||||
| 	ErrDecode:                  "Invalid message", | ||||
| 	ErrInvalidMsgCode:          "Invalid message code", | ||||
| 	ErrProtocolVersionMismatch: "Protocol version mismatch", | ||||
| 	ErrNetworkIdMismatch:       "NetworkId mismatch", | ||||
| 	ErrGenesisBlockMismatch:    "Genesis block mismatch", | ||||
| 	ErrNetworkIDMismatch:       "Network ID mismatch", | ||||
| 	ErrGenesisMismatch:         "Genesis mismatch", | ||||
| 	ErrForkIDRejected:          "Fork ID rejected", | ||||
| 	ErrNoStatusMsg:             "No status message", | ||||
| 	ErrExtraStatusMsg:          "Extra status message", | ||||
| 	ErrSuspendedPeer:           "Suspended peer", | ||||
| } | ||||
|  | ||||
| type txPool interface { | ||||
| @@ -108,8 +106,8 @@ type txPool interface { | ||||
| 	SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription | ||||
| } | ||||
|  | ||||
| // statusData is the network packet for the status message. | ||||
| type statusData struct { | ||||
| // statusData63 is the network packet for the status message for eth/63. | ||||
| type statusData63 struct { | ||||
| 	ProtocolVersion uint32 | ||||
| 	NetworkId       uint64 | ||||
| 	TD              *big.Int | ||||
| @@ -117,6 +115,16 @@ type statusData struct { | ||||
| 	GenesisBlock    common.Hash | ||||
| } | ||||
|  | ||||
| // statusData is the network packet for the status message for eth/64 and later. | ||||
| type statusData struct { | ||||
| 	ProtocolVersion uint32 | ||||
| 	NetworkID       uint64 | ||||
| 	TD              *big.Int | ||||
| 	Head            common.Hash | ||||
| 	Genesis         common.Hash | ||||
| 	ForkID          forkid.ID | ||||
| } | ||||
|  | ||||
| // newBlockHashesData is the network packet for the block announcements. | ||||
| type newBlockHashesData []struct { | ||||
| 	Hash   common.Hash // Hash of one particular block being announced | ||||
|   | ||||
| @@ -18,15 +18,24 @@ package eth | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"math/big" | ||||
| 	"sync" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/ethereum/go-ethereum/common" | ||||
| 	"github.com/ethereum/go-ethereum/consensus/ethash" | ||||
| 	"github.com/ethereum/go-ethereum/core" | ||||
| 	"github.com/ethereum/go-ethereum/core/forkid" | ||||
| 	"github.com/ethereum/go-ethereum/core/rawdb" | ||||
| 	"github.com/ethereum/go-ethereum/core/types" | ||||
| 	"github.com/ethereum/go-ethereum/core/vm" | ||||
| 	"github.com/ethereum/go-ethereum/crypto" | ||||
| 	"github.com/ethereum/go-ethereum/eth/downloader" | ||||
| 	"github.com/ethereum/go-ethereum/event" | ||||
| 	"github.com/ethereum/go-ethereum/p2p" | ||||
| 	"github.com/ethereum/go-ethereum/p2p/enode" | ||||
| 	"github.com/ethereum/go-ethereum/params" | ||||
| 	"github.com/ethereum/go-ethereum/rlp" | ||||
| ) | ||||
|  | ||||
| @@ -37,10 +46,7 @@ func init() { | ||||
| var testAccount, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") | ||||
|  | ||||
| // Tests that handshake failures are detected and reported correctly. | ||||
| func TestStatusMsgErrors62(t *testing.T) { testStatusMsgErrors(t, 62) } | ||||
| func TestStatusMsgErrors63(t *testing.T) { testStatusMsgErrors(t, 63) } | ||||
|  | ||||
| func testStatusMsgErrors(t *testing.T, protocol int) { | ||||
| func TestStatusMsgErrors63(t *testing.T) { | ||||
| 	pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil) | ||||
| 	var ( | ||||
| 		genesis = pm.blockchain.Genesis() | ||||
| @@ -59,21 +65,20 @@ func testStatusMsgErrors(t *testing.T, protocol int) { | ||||
| 			wantError: errResp(ErrNoStatusMsg, "first msg has code 2 (!= 0)"), | ||||
| 		}, | ||||
| 		{ | ||||
| 			code: StatusMsg, data: statusData{10, DefaultConfig.NetworkId, td, head.Hash(), genesis.Hash()}, | ||||
| 			wantError: errResp(ErrProtocolVersionMismatch, "10 (!= %d)", protocol), | ||||
| 			code: StatusMsg, data: statusData63{10, DefaultConfig.NetworkId, td, head.Hash(), genesis.Hash()}, | ||||
| 			wantError: errResp(ErrProtocolVersionMismatch, "10 (!= %d)", 63), | ||||
| 		}, | ||||
| 		{ | ||||
| 			code: StatusMsg, data: statusData{uint32(protocol), 999, td, head.Hash(), genesis.Hash()}, | ||||
| 			wantError: errResp(ErrNetworkIdMismatch, "999 (!= %d)", DefaultConfig.NetworkId), | ||||
| 			code: StatusMsg, data: statusData63{63, 999, td, head.Hash(), genesis.Hash()}, | ||||
| 			wantError: errResp(ErrNetworkIDMismatch, "999 (!= %d)", DefaultConfig.NetworkId), | ||||
| 		}, | ||||
| 		{ | ||||
| 			code: StatusMsg, data: statusData{uint32(protocol), DefaultConfig.NetworkId, td, head.Hash(), common.Hash{3}}, | ||||
| 			wantError: errResp(ErrGenesisBlockMismatch, "0300000000000000 (!= %x)", genesis.Hash().Bytes()[:8]), | ||||
| 			code: StatusMsg, data: statusData63{63, DefaultConfig.NetworkId, td, head.Hash(), common.Hash{3}}, | ||||
| 			wantError: errResp(ErrGenesisMismatch, "0300000000000000 (!= %x)", genesis.Hash().Bytes()[:8]), | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for i, test := range tests { | ||||
| 		p, errc := newTestPeer("peer", protocol, pm, false) | ||||
| 		p, errc := newTestPeer("peer", 63, pm, false) | ||||
| 		// The send call might hang until reset because | ||||
| 		// the protocol might not read the payload. | ||||
| 		go p2p.Send(p.app, test.code, test.data) | ||||
| @@ -92,9 +97,155 @@ func testStatusMsgErrors(t *testing.T, protocol int) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestStatusMsgErrors64(t *testing.T) { | ||||
| 	pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil) | ||||
| 	var ( | ||||
| 		genesis = pm.blockchain.Genesis() | ||||
| 		head    = pm.blockchain.CurrentHeader() | ||||
| 		td      = pm.blockchain.GetTd(head.Hash(), head.Number.Uint64()) | ||||
| 		forkID  = forkid.NewID(pm.blockchain) | ||||
| 	) | ||||
| 	defer pm.Stop() | ||||
|  | ||||
| 	tests := []struct { | ||||
| 		code      uint64 | ||||
| 		data      interface{} | ||||
| 		wantError error | ||||
| 	}{ | ||||
| 		{ | ||||
| 			code: TxMsg, data: []interface{}{}, | ||||
| 			wantError: errResp(ErrNoStatusMsg, "first msg has code 2 (!= 0)"), | ||||
| 		}, | ||||
| 		{ | ||||
| 			code: StatusMsg, data: statusData{10, DefaultConfig.NetworkId, td, head.Hash(), genesis.Hash(), forkID}, | ||||
| 			wantError: errResp(ErrProtocolVersionMismatch, "10 (!= %d)", 64), | ||||
| 		}, | ||||
| 		{ | ||||
| 			code: StatusMsg, data: statusData{64, 999, td, head.Hash(), genesis.Hash(), forkID}, | ||||
| 			wantError: errResp(ErrNetworkIDMismatch, "999 (!= %d)", DefaultConfig.NetworkId), | ||||
| 		}, | ||||
| 		{ | ||||
| 			code: StatusMsg, data: statusData{64, DefaultConfig.NetworkId, td, head.Hash(), common.Hash{3}, forkID}, | ||||
| 			wantError: errResp(ErrGenesisMismatch, "0300000000000000000000000000000000000000000000000000000000000000 (!= %x)", genesis.Hash()), | ||||
| 		}, | ||||
| 		{ | ||||
| 			code: StatusMsg, data: statusData{64, DefaultConfig.NetworkId, td, head.Hash(), genesis.Hash(), forkid.ID{Hash: [4]byte{0x00, 0x01, 0x02, 0x03}}}, | ||||
| 			wantError: errResp(ErrForkIDRejected, forkid.ErrLocalIncompatibleOrStale.Error()), | ||||
| 		}, | ||||
| 	} | ||||
| 	for i, test := range tests { | ||||
| 		p, errc := newTestPeer("peer", 64, pm, false) | ||||
| 		// The send call might hang until reset because | ||||
| 		// the protocol might not read the payload. | ||||
| 		go p2p.Send(p.app, test.code, test.data) | ||||
|  | ||||
| 		select { | ||||
| 		case err := <-errc: | ||||
| 			if err == nil { | ||||
| 				t.Errorf("test %d: protocol returned nil error, want %q", i, test.wantError) | ||||
| 			} else if err.Error() != test.wantError.Error() { | ||||
| 				t.Errorf("test %d: wrong error: got %q, want %q", i, err, test.wantError) | ||||
| 			} | ||||
| 		case <-time.After(2 * time.Second): | ||||
| 			t.Errorf("protocol did not shut down within 2 seconds") | ||||
| 		} | ||||
| 		p.close() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestForkIDSplit(t *testing.T) { | ||||
| 	var ( | ||||
| 		engine = ethash.NewFaker() | ||||
|  | ||||
| 		configNoFork  = ¶ms.ChainConfig{HomesteadBlock: big.NewInt(1)} | ||||
| 		configProFork = ¶ms.ChainConfig{ | ||||
| 			HomesteadBlock: big.NewInt(1), | ||||
| 			EIP150Block:    big.NewInt(2), | ||||
| 			EIP155Block:    big.NewInt(2), | ||||
| 			EIP158Block:    big.NewInt(2), | ||||
| 			ByzantiumBlock: big.NewInt(3), | ||||
| 		} | ||||
| 		dbNoFork  = rawdb.NewMemoryDatabase() | ||||
| 		dbProFork = rawdb.NewMemoryDatabase() | ||||
|  | ||||
| 		gspecNoFork  = &core.Genesis{Config: configNoFork} | ||||
| 		gspecProFork = &core.Genesis{Config: configProFork} | ||||
|  | ||||
| 		genesisNoFork  = gspecNoFork.MustCommit(dbNoFork) | ||||
| 		genesisProFork = gspecProFork.MustCommit(dbProFork) | ||||
|  | ||||
| 		chainNoFork, _  = core.NewBlockChain(dbNoFork, nil, configNoFork, engine, vm.Config{}, nil) | ||||
| 		chainProFork, _ = core.NewBlockChain(dbProFork, nil, configProFork, engine, vm.Config{}, nil) | ||||
|  | ||||
| 		blocksNoFork, _  = core.GenerateChain(configNoFork, genesisNoFork, engine, dbNoFork, 2, nil) | ||||
| 		blocksProFork, _ = core.GenerateChain(configProFork, genesisProFork, engine, dbProFork, 2, nil) | ||||
|  | ||||
| 		ethNoFork, _  = NewProtocolManager(configNoFork, nil, downloader.FullSync, 1, new(event.TypeMux), new(testTxPool), engine, chainNoFork, dbNoFork, 1, nil) | ||||
| 		ethProFork, _ = NewProtocolManager(configProFork, nil, downloader.FullSync, 1, new(event.TypeMux), new(testTxPool), engine, chainProFork, dbProFork, 1, nil) | ||||
| 	) | ||||
| 	ethNoFork.Start(1000) | ||||
| 	ethProFork.Start(1000) | ||||
|  | ||||
| 	// Both nodes should allow the other to connect (same genesis, next fork is the same) | ||||
| 	p2pNoFork, p2pProFork := p2p.MsgPipe() | ||||
| 	peerNoFork := newPeer(64, p2p.NewPeer(enode.ID{1}, "", nil), p2pNoFork) | ||||
| 	peerProFork := newPeer(64, p2p.NewPeer(enode.ID{2}, "", nil), p2pProFork) | ||||
|  | ||||
| 	errc := make(chan error, 2) | ||||
| 	go func() { errc <- ethNoFork.handle(peerProFork) }() | ||||
| 	go func() { errc <- ethProFork.handle(peerNoFork) }() | ||||
|  | ||||
| 	select { | ||||
| 	case err := <-errc: | ||||
| 		t.Fatalf("frontier nofork <-> profork failed: %v", err) | ||||
| 	case <-time.After(250 * time.Millisecond): | ||||
| 		p2pNoFork.Close() | ||||
| 		p2pProFork.Close() | ||||
| 	} | ||||
| 	// Progress into Homestead. Fork's match, so we don't care what the future holds | ||||
| 	chainNoFork.InsertChain(blocksNoFork[:1]) | ||||
| 	chainProFork.InsertChain(blocksProFork[:1]) | ||||
|  | ||||
| 	p2pNoFork, p2pProFork = p2p.MsgPipe() | ||||
| 	peerNoFork = newPeer(64, p2p.NewPeer(enode.ID{1}, "", nil), p2pNoFork) | ||||
| 	peerProFork = newPeer(64, p2p.NewPeer(enode.ID{2}, "", nil), p2pProFork) | ||||
|  | ||||
| 	errc = make(chan error, 2) | ||||
| 	go func() { errc <- ethNoFork.handle(peerProFork) }() | ||||
| 	go func() { errc <- ethProFork.handle(peerNoFork) }() | ||||
|  | ||||
| 	select { | ||||
| 	case err := <-errc: | ||||
| 		t.Fatalf("homestead nofork <-> profork failed: %v", err) | ||||
| 	case <-time.After(250 * time.Millisecond): | ||||
| 		p2pNoFork.Close() | ||||
| 		p2pProFork.Close() | ||||
| 	} | ||||
| 	// Progress into Spurious. Forks mismatch, signalling differing chains, reject | ||||
| 	chainNoFork.InsertChain(blocksNoFork[1:2]) | ||||
| 	chainProFork.InsertChain(blocksProFork[1:2]) | ||||
|  | ||||
| 	p2pNoFork, p2pProFork = p2p.MsgPipe() | ||||
| 	peerNoFork = newPeer(64, p2p.NewPeer(enode.ID{1}, "", nil), p2pNoFork) | ||||
| 	peerProFork = newPeer(64, p2p.NewPeer(enode.ID{2}, "", nil), p2pProFork) | ||||
|  | ||||
| 	errc = make(chan error, 2) | ||||
| 	go func() { errc <- ethNoFork.handle(peerProFork) }() | ||||
| 	go func() { errc <- ethProFork.handle(peerNoFork) }() | ||||
|  | ||||
| 	select { | ||||
| 	case err := <-errc: | ||||
| 		if want := errResp(ErrForkIDRejected, forkid.ErrLocalIncompatibleOrStale.Error()); err.Error() != want.Error() { | ||||
| 			t.Fatalf("fork ID rejection error mismatch: have %v, want %v", err, want) | ||||
| 		} | ||||
| 	case <-time.After(250 * time.Millisecond): | ||||
| 		t.Fatalf("split peers not rejected") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // This test checks that received transactions are added to the local pool. | ||||
| func TestRecvTransactions62(t *testing.T) { testRecvTransactions(t, 62) } | ||||
| func TestRecvTransactions63(t *testing.T) { testRecvTransactions(t, 63) } | ||||
| func TestRecvTransactions64(t *testing.T) { testRecvTransactions(t, 64) } | ||||
|  | ||||
| func testRecvTransactions(t *testing.T, protocol int) { | ||||
| 	txAdded := make(chan []*types.Transaction) | ||||
| @@ -121,8 +272,8 @@ func testRecvTransactions(t *testing.T, protocol int) { | ||||
| } | ||||
|  | ||||
| // This test checks that pending transactions are sent. | ||||
| func TestSendTransactions62(t *testing.T) { testSendTransactions(t, 62) } | ||||
| func TestSendTransactions63(t *testing.T) { testSendTransactions(t, 63) } | ||||
| func TestSendTransactions64(t *testing.T) { testSendTransactions(t, 64) } | ||||
|  | ||||
| func testSendTransactions(t *testing.T, protocol int) { | ||||
| 	pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user