github.com/karalabe/go-ethereum@v0.8.5/eth/protocol.go (about)

     1  package eth
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"math/big"
     8  
     9  	"github.com/ethereum/go-ethereum/core/types"
    10  	"github.com/ethereum/go-ethereum/ethutil"
    11  	"github.com/ethereum/go-ethereum/p2p"
    12  	"github.com/ethereum/go-ethereum/rlp"
    13  )
    14  
    15  const (
    16  	ProtocolVersion    = 54
    17  	NetworkId          = 0
    18  	ProtocolLength     = uint64(8)
    19  	ProtocolMaxMsgSize = 10 * 1024 * 1024
    20  )
    21  
    22  // eth protocol message codes
    23  const (
    24  	StatusMsg = iota
    25  	GetTxMsg  // unused
    26  	TxMsg
    27  	GetBlockHashesMsg
    28  	BlockHashesMsg
    29  	GetBlocksMsg
    30  	BlocksMsg
    31  	NewBlockMsg
    32  )
    33  
    34  // ethProtocol represents the ethereum wire protocol
    35  // instance is running on each peer
    36  type ethProtocol struct {
    37  	txPool       txPool
    38  	chainManager chainManager
    39  	blockPool    blockPool
    40  	peer         *p2p.Peer
    41  	id           string
    42  	rw           p2p.MsgReadWriter
    43  }
    44  
    45  // backend is the interface the ethereum protocol backend should implement
    46  // used as an argument to EthProtocol
    47  type txPool interface {
    48  	AddTransactions([]*types.Transaction)
    49  	GetTransactions() types.Transactions
    50  }
    51  
    52  type chainManager interface {
    53  	GetBlockHashesFromHash(hash []byte, amount uint64) (hashes [][]byte)
    54  	GetBlock(hash []byte) (block *types.Block)
    55  	Status() (td *big.Int, currentBlock []byte, genesisBlock []byte)
    56  }
    57  
    58  type blockPool interface {
    59  	AddBlockHashes(next func() ([]byte, bool), peerId string)
    60  	AddBlock(block *types.Block, peerId string)
    61  	AddPeer(td *big.Int, currentBlock []byte, peerId string, requestHashes func([]byte) error, requestBlocks func([][]byte) error, peerError func(int, string, ...interface{})) (best bool)
    62  	RemovePeer(peerId string)
    63  }
    64  
    65  // message structs used for rlp decoding
    66  type newBlockMsgData struct {
    67  	Block *types.Block
    68  	TD    *big.Int
    69  }
    70  
    71  const maxHashes = 255
    72  
    73  type getBlockHashesMsgData struct {
    74  	Hash   []byte
    75  	Amount uint64
    76  }
    77  
    78  // main entrypoint, wrappers starting a server running the eth protocol
    79  // use this constructor to attach the protocol ("class") to server caps
    80  // the Dev p2p layer then runs the protocol instance on each peer
    81  func EthProtocol(txPool txPool, chainManager chainManager, blockPool blockPool) p2p.Protocol {
    82  	return p2p.Protocol{
    83  		Name:    "eth",
    84  		Version: ProtocolVersion,
    85  		Length:  ProtocolLength,
    86  		Run: func(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
    87  			return runEthProtocol(txPool, chainManager, blockPool, peer, rw)
    88  		},
    89  	}
    90  }
    91  
    92  // the main loop that handles incoming messages
    93  // note RemovePeer in the post-disconnect hook
    94  func runEthProtocol(txPool txPool, chainManager chainManager, blockPool blockPool, peer *p2p.Peer, rw p2p.MsgReadWriter) (err error) {
    95  	id := peer.ID()
    96  	self := &ethProtocol{
    97  		txPool:       txPool,
    98  		chainManager: chainManager,
    99  		blockPool:    blockPool,
   100  		rw:           rw,
   101  		peer:         peer,
   102  		id:           fmt.Sprintf("%x", id[:8]),
   103  	}
   104  	err = self.handleStatus()
   105  	if err == nil {
   106  		self.propagateTxs()
   107  		for {
   108  			err = self.handle()
   109  			if err != nil {
   110  				self.blockPool.RemovePeer(self.id)
   111  				break
   112  			}
   113  		}
   114  	}
   115  	return
   116  }
   117  
   118  func (self *ethProtocol) handle() error {
   119  	msg, err := self.rw.ReadMsg()
   120  	if err != nil {
   121  		return err
   122  	}
   123  	if msg.Size > ProtocolMaxMsgSize {
   124  		return self.protoError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
   125  	}
   126  	// make sure that the payload has been fully consumed
   127  	defer msg.Discard()
   128  
   129  	switch msg.Code {
   130  	case GetTxMsg: // ignore
   131  	case StatusMsg:
   132  		return self.protoError(ErrExtraStatusMsg, "")
   133  
   134  	case TxMsg:
   135  		// TODO: rework using lazy RLP stream
   136  		var txs []*types.Transaction
   137  		if err := msg.Decode(&txs); err != nil {
   138  			return self.protoError(ErrDecode, "msg %v: %v", msg, err)
   139  		}
   140  		self.txPool.AddTransactions(txs)
   141  
   142  	case GetBlockHashesMsg:
   143  		var request getBlockHashesMsgData
   144  		if err := msg.Decode(&request); err != nil {
   145  			return self.protoError(ErrDecode, "->msg %v: %v", msg, err)
   146  		}
   147  
   148  		//request.Amount = uint64(math.Min(float64(maxHashes), float64(request.Amount)))
   149  		if request.Amount > maxHashes {
   150  			request.Amount = maxHashes
   151  		}
   152  		hashes := self.chainManager.GetBlockHashesFromHash(request.Hash, request.Amount)
   153  		return p2p.EncodeMsg(self.rw, BlockHashesMsg, ethutil.ByteSliceToInterface(hashes)...)
   154  
   155  	case BlockHashesMsg:
   156  		// TODO: redo using lazy decode , this way very inefficient on known chains
   157  		msgStream := rlp.NewStream(msg.Payload)
   158  		var err error
   159  		var i int
   160  
   161  		iter := func() (hash []byte, ok bool) {
   162  			hash, err = msgStream.Bytes()
   163  			if err == nil {
   164  				i++
   165  				ok = true
   166  			} else {
   167  				if err != io.EOF {
   168  					self.protoError(ErrDecode, "msg %v: after %v hashes : %v", msg, i, err)
   169  				}
   170  			}
   171  			return
   172  		}
   173  
   174  		self.blockPool.AddBlockHashes(iter, self.id)
   175  
   176  	case GetBlocksMsg:
   177  		msgStream := rlp.NewStream(msg.Payload)
   178  		var blocks []interface{}
   179  		var i int
   180  		for {
   181  			i++
   182  			var hash []byte
   183  			if err := msgStream.Decode(&hash); err != nil {
   184  				if err == io.EOF {
   185  					break
   186  				} else {
   187  					return self.protoError(ErrDecode, "msg %v: %v", msg, err)
   188  				}
   189  			}
   190  			block := self.chainManager.GetBlock(hash)
   191  			if block != nil {
   192  				blocks = append(blocks, block)
   193  			}
   194  			if i == blockHashesBatchSize {
   195  				break
   196  			}
   197  		}
   198  		return p2p.EncodeMsg(self.rw, BlocksMsg, blocks...)
   199  
   200  	case BlocksMsg:
   201  		msgStream := rlp.NewStream(msg.Payload)
   202  		for {
   203  			var block types.Block
   204  			if err := msgStream.Decode(&block); err != nil {
   205  				if err == io.EOF {
   206  					break
   207  				} else {
   208  					return self.protoError(ErrDecode, "msg %v: %v", msg, err)
   209  				}
   210  			}
   211  			self.blockPool.AddBlock(&block, self.id)
   212  		}
   213  
   214  	case NewBlockMsg:
   215  		var request newBlockMsgData
   216  		if err := msg.Decode(&request); err != nil {
   217  			return self.protoError(ErrDecode, "msg %v: %v", msg, err)
   218  		}
   219  		hash := request.Block.Hash()
   220  		// to simplify backend interface adding a new block
   221  		// uses AddPeer followed by AddHashes, AddBlock only if peer is the best peer
   222  		// (or selected as new best peer)
   223  		if self.blockPool.AddPeer(request.TD, hash, self.id, self.requestBlockHashes, self.requestBlocks, self.protoErrorDisconnect) {
   224  			self.blockPool.AddBlock(request.Block, self.id)
   225  		}
   226  
   227  	default:
   228  		return self.protoError(ErrInvalidMsgCode, "%v", msg.Code)
   229  	}
   230  	return nil
   231  }
   232  
   233  type statusMsgData struct {
   234  	ProtocolVersion uint32
   235  	NetworkId       uint32
   236  	TD              *big.Int
   237  	CurrentBlock    []byte
   238  	GenesisBlock    []byte
   239  }
   240  
   241  func (self *ethProtocol) statusMsg() p2p.Msg {
   242  	td, currentBlock, genesisBlock := self.chainManager.Status()
   243  
   244  	return p2p.NewMsg(StatusMsg,
   245  		uint32(ProtocolVersion),
   246  		uint32(NetworkId),
   247  		td,
   248  		currentBlock,
   249  		genesisBlock,
   250  	)
   251  }
   252  
   253  func (self *ethProtocol) handleStatus() error {
   254  	// send precanned status message
   255  	if err := self.rw.WriteMsg(self.statusMsg()); err != nil {
   256  		return err
   257  	}
   258  
   259  	// read and handle remote status
   260  	msg, err := self.rw.ReadMsg()
   261  	if err != nil {
   262  		return err
   263  	}
   264  
   265  	if msg.Code != StatusMsg {
   266  		return self.protoError(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg)
   267  	}
   268  
   269  	if msg.Size > ProtocolMaxMsgSize {
   270  		return self.protoError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
   271  	}
   272  
   273  	var status statusMsgData
   274  	if err := msg.Decode(&status); err != nil {
   275  		return self.protoError(ErrDecode, "msg %v: %v", msg, err)
   276  	}
   277  
   278  	_, _, genesisBlock := self.chainManager.Status()
   279  
   280  	if bytes.Compare(status.GenesisBlock, genesisBlock) != 0 {
   281  		return self.protoError(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock, genesisBlock)
   282  	}
   283  
   284  	if status.NetworkId != NetworkId {
   285  		return self.protoError(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, NetworkId)
   286  	}
   287  
   288  	if ProtocolVersion != status.ProtocolVersion {
   289  		return self.protoError(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, ProtocolVersion)
   290  	}
   291  
   292  	self.peer.Infof("Peer is [eth] capable (%d/%d). TD=%v H=%x\n", status.ProtocolVersion, status.NetworkId, status.TD, status.CurrentBlock[:4])
   293  
   294  	self.blockPool.AddPeer(status.TD, status.CurrentBlock, self.id, self.requestBlockHashes, self.requestBlocks, self.protoErrorDisconnect)
   295  
   296  	return nil
   297  }
   298  
   299  func (self *ethProtocol) requestBlockHashes(from []byte) error {
   300  	self.peer.Debugf("fetching hashes (%d) %x...\n", blockHashesBatchSize, from[0:4])
   301  	return p2p.EncodeMsg(self.rw, GetBlockHashesMsg, interface{}(from), uint64(blockHashesBatchSize))
   302  }
   303  
   304  func (self *ethProtocol) requestBlocks(hashes [][]byte) error {
   305  	self.peer.Debugf("fetching %v blocks", len(hashes))
   306  	return p2p.EncodeMsg(self.rw, GetBlocksMsg, ethutil.ByteSliceToInterface(hashes)...)
   307  }
   308  
   309  func (self *ethProtocol) protoError(code int, format string, params ...interface{}) (err *protocolError) {
   310  	err = ProtocolError(code, format, params...)
   311  	if err.Fatal() {
   312  		self.peer.Errorln("err %v", err)
   313  		// disconnect
   314  	} else {
   315  		self.peer.Debugf("fyi %v", err)
   316  	}
   317  	return
   318  }
   319  
   320  func (self *ethProtocol) protoErrorDisconnect(code int, format string, params ...interface{}) {
   321  	err := ProtocolError(code, format, params...)
   322  	if err.Fatal() {
   323  		self.peer.Errorln("err %v", err)
   324  		// disconnect
   325  	} else {
   326  		self.peer.Debugf("fyi %v", err)
   327  	}
   328  
   329  }
   330  
   331  func (self *ethProtocol) propagateTxs() {
   332  	transactions := self.txPool.GetTransactions()
   333  	iface := make([]interface{}, len(transactions))
   334  	for i, transaction := range transactions {
   335  		iface[i] = transaction
   336  	}
   337  
   338  	self.rw.WriteMsg(p2p.NewMsg(TxMsg, iface...))
   339  }