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 := ðProtocol{ 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 }