func readHandshakeMsg(msg plainDecoder, plainSize int, prv *ecdsa.PrivateKey, r io.Reader) ([]byte, error) { buf := make([]byte, plainSize) if _, err := io.ReadFull(r, buf); err != nil { return buf, err } // Attempt decoding pre-EIP-8 "plain" format. key := ecies.ImportECDSA(prv) if dec, err := key.Decrypt(rand.Reader, buf, nil, nil); err == nil { msg.decodePlain(dec) return buf, nil } // Could be EIP-8 format, try that. prefix := buf[:2] size := binary.BigEndian.Uint16(prefix) if size < uint16(plainSize) { return buf, fmt.Errorf("size underflow, need at least %d bytes", plainSize) } buf = append(buf, make([]byte, size-uint16(plainSize)+2)...) if _, err := io.ReadFull(r, buf[plainSize:]); err != nil { return buf, err } dec, err := key.Decrypt(rand.Reader, buf[2:], nil, prefix) if err != nil { return buf, err } // Can't use rlp.DecodeBytes here because it rejects // trailing data (forward-compatibility). s := rlp.NewStream(bytes.NewReader(dec), 0) return buf, s.Decode(msg) }
// Decode parses the RLP content of a message into // the given value, which must be a pointer. // // For the decoding rules, please see package rlp. func (msg Msg) Decode(val interface{}) error { s := rlp.NewStream(msg.Payload, uint64(msg.Size)) if err := s.Decode(val); err != nil { return newPeerError(errInvalidMsg, "(code %x) (size %d) %v", msg.Code, msg.Size, err) } return nil }
// handshake sends the protocol initiation status message to the remote peer and // verifies the remote status too. func (self *peer) handshake() error { // Send the handshake status message asynchronously errc := make(chan error, 1) go func() { errc <- p2p.SendItems(self.ws, statusCode, protocolVersion) }() // Fetch the remote status packet and verify protocol match packet, err := self.ws.ReadMsg() if err != nil { return err } if packet.Code != statusCode { return fmt.Errorf("peer sent %x before status packet", packet.Code) } s := rlp.NewStream(packet.Payload, uint64(packet.Size)) if _, err := s.List(); err != nil { return fmt.Errorf("bad status message: %v", err) } peerVersion, err := s.Uint() if err != nil { return fmt.Errorf("bad status message: %v", err) } if peerVersion != protocolVersion { return fmt.Errorf("protocol version mismatch %d != %d", peerVersion, protocolVersion) } // Wait until out own status is consumed too if err := <-errc; err != nil { return fmt.Errorf("failed to send status packet: %v", err) } return nil }
func decodePacket(buf []byte) (packet, NodeID, []byte, error) { if len(buf) < headSize+1 { return nil, NodeID{}, nil, errPacketTooSmall } hash, sig, sigdata := buf[:macSize], buf[macSize:headSize], buf[headSize:] shouldhash := crypto.Keccak256(buf[macSize:]) if !bytes.Equal(hash, shouldhash) { return nil, NodeID{}, nil, errBadHash } fromID, err := recoverNodeID(crypto.Keccak256(buf[headSize:]), sig) if err != nil { return nil, NodeID{}, hash, err } var req packet switch ptype := sigdata[0]; ptype { case pingPacket: req = new(ping) case pongPacket: req = new(pong) case findnodePacket: req = new(findnode) case neighborsPacket: req = new(neighbors) default: return nil, fromID, hash, fmt.Errorf("unknown type: %d", ptype) } s := rlp.NewStream(bytes.NewReader(sigdata[1:]), 0) err = s.Decode(req) return req, fromID, hash, err }
func (app *EthereumApplication) AppendTx(txBytes []byte) (retCode types.RetCode, result []byte, log string) { // decode and run tx tx := new(ethtypes.Transaction) rlpStream := rlp.NewStream(bytes.NewBuffer(txBytes), 0) if err := tx.DecodeRLP(rlpStream); err != nil { return types.RetCodeEncodingError, result, log } gpi := big.NewInt(1000000000) // a billion ... TODO: configurable gp := core.GasPool(*gpi) // XXX: this feels so wrong!? ret, gas, err := core.ApplyMessage(NewEnv(app.stateDB, tx), tx, &gp) if err != nil { if err == ethtypes.ErrInvalidSig || err == ethtypes.ErrInvalidPubKey { return types.RetCodeUnauthorized, result, err.Error() } else if core.IsNonceErr(err) { return types.RetCodeBadNonce, result, err.Error() } else if core.IsInvalidTxErr(err) { return types.RetCodeInsufficientFees, result, err.Error() // bad gas or value transfer } else { return types.RetCodeUnauthorized, result, err.Error() // bad pubkey recovery } } _, _ = ret, gas return types.RetCodeOK, result, log }
func ImportChain(chainmgr *core.ChainManager, fn string) error { fmt.Printf("importing blockchain '%s'\n", fn) fh, err := os.OpenFile(fn, os.O_RDONLY, os.ModePerm) if err != nil { return err } defer fh.Close() chainmgr.Reset() stream := rlp.NewStream(fh, 0) var i, n int batchSize := 2500 blocks := make(types.Blocks, batchSize) for ; ; i++ { var b types.Block if err := stream.Decode(&b); err == io.EOF { break } else if err != nil { return fmt.Errorf("at block %d: %v", i, err) } blocks[n] = &b n++ if n == batchSize { if _, err := chainmgr.InsertChain(blocks); err != nil { return fmt.Errorf("invalid block %v", err) } n = 0 blocks = make(types.Blocks, batchSize) } } if n > 0 { if _, err := chainmgr.InsertChain(blocks[:n]); err != nil { return fmt.Errorf("invalid block %v", err) } } fmt.Printf("imported %d blocks\n", i) return nil }
func (self *adminApi) ImportChain(req *shared.Request) (interface{}, error) { args := new(ImportExportChainArgs) if err := self.coder.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) } fh, err := os.Open(args.Filename) if err != nil { return false, err } defer fh.Close() stream := rlp.NewStream(fh, 0) // Run actual the import. blocks := make(types.Blocks, importBatchSize) n := 0 for batch := 0; ; batch++ { i := 0 for ; i < importBatchSize; i++ { var b types.Block if err := stream.Decode(&b); err == io.EOF { break } else if err != nil { return false, fmt.Errorf("at block %d: %v", n, err) } blocks[i] = &b n++ } if i == 0 { break } // Import the batch. if hasAllBlocks(self.ethereum.ChainManager(), blocks[:i]) { continue } if _, err := self.ethereum.ChainManager().InsertChain(blocks[:i]); err != nil { return false, fmt.Errorf("invalid block %d: %v", n, err) } } return true, nil }
func main() { flag.Parse() var r io.Reader switch { case *hexMode != "": data, err := hex.DecodeString(*hexMode) if err != nil { die(err) } r = bytes.NewReader(data) case flag.NArg() == 0: r = os.Stdin case flag.NArg() == 1: fd, err := os.Open(flag.Arg(0)) if err != nil { die(err) } defer fd.Close() r = fd default: fmt.Fprintln(os.Stderr, "Error: too many arguments") flag.Usage() os.Exit(2) } s := rlp.NewStream(r, 0) for { if err := dump(s, 0); err != nil { if err != io.EOF { die(err) } break } fmt.Println() } }
// Run executes the test. func (t *RLPTest) Run() error { outb, err := hex.DecodeString(t.Out) if err != nil { return fmt.Errorf("invalid hex in Out") } // Handle simple decoding tests with no actual In value. if t.In == "VALID" || t.In == "INVALID" { return checkDecodeInterface(outb, t.In == "VALID") } // Check whether encoding the value produces the same bytes. in := translateJSON(t.In) b, err := rlp.EncodeToBytes(in) if err != nil { return fmt.Errorf("encode failed: %v", err) } if !bytes.Equal(b, outb) { return fmt.Errorf("encode produced %x, want %x", b, outb) } // Test stream decoding. s := rlp.NewStream(bytes.NewReader(outb), 0) return checkDecodeFromJSON(s, in) }
func ImportChain(chain *core.BlockChain, fn string) error { // Watch for Ctrl-C while the import is running. // If a signal is received, the import will stop at the next batch. interrupt := make(chan os.Signal, 1) stop := make(chan struct{}) signal.Notify(interrupt, os.Interrupt) defer signal.Stop(interrupt) defer close(interrupt) go func() { if _, ok := <-interrupt; ok { glog.Info("caught interrupt during import, will stop at next batch") } close(stop) }() checkInterrupt := func() bool { select { case <-stop: return true default: return false } } glog.Infoln("Importing blockchain", fn) fh, err := os.Open(fn) if err != nil { return err } defer fh.Close() stream := rlp.NewStream(fh, 0) // Run actual the import. blocks := make(types.Blocks, importBatchSize) n := 0 for batch := 0; ; batch++ { // Load a batch of RLP blocks. if checkInterrupt() { return fmt.Errorf("interrupted") } i := 0 for ; i < importBatchSize; i++ { var b types.Block if err := stream.Decode(&b); err == io.EOF { break } else if err != nil { return fmt.Errorf("at block %d: %v", n, err) } // don't import first block if b.NumberU64() == 0 { i-- continue } blocks[i] = &b n++ } if i == 0 { break } // Import the batch. if checkInterrupt() { return fmt.Errorf("interrupted") } if hasAllBlocks(chain, blocks[:i]) { glog.Infof("skipping batch %d, all blocks present [%x / %x]", batch, blocks[0].Hash().Bytes()[:4], blocks[i-1].Hash().Bytes()[:4]) continue } if _, err := chain.InsertChain(blocks[:i]); err != nil { return fmt.Errorf("invalid block %d: %v", n, err) } } return nil }
// handleMsg is invoked whenever an inbound message is received from a remote // peer. The remote connection is torn down upon returning any error. func (pm *ProtocolManager) handleMsg(p *peer) error { // Read the next message from the remote peer, and ensure it's fully consumed msg, err := p.rw.ReadMsg() if err != nil { return err } if msg.Size > ProtocolMaxMsgSize { return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) } defer msg.Discard() // Handle the message depending on its contents switch { case msg.Code == StatusMsg: // Status messages should never arrive after the handshake return errResp(ErrExtraStatusMsg, "uncontrolled status message") case p.version < eth62 && msg.Code == GetBlockHashesMsg: // Retrieve the number of hashes to return and from which origin hash var request getBlockHashesData if err := msg.Decode(&request); err != nil { return errResp(ErrDecode, "%v: %v", msg, err) } if request.Amount > uint64(downloader.MaxHashFetch) { request.Amount = uint64(downloader.MaxHashFetch) } // Retrieve the hashes from the block chain and return them hashes := pm.blockchain.GetBlockHashesFromHash(request.Hash, request.Amount) if len(hashes) == 0 { glog.V(logger.Debug).Infof("invalid block hash %x", request.Hash.Bytes()[:4]) } return p.SendBlockHashes(hashes) case p.version < eth62 && msg.Code == GetBlockHashesFromNumberMsg: // Retrieve and decode the number of hashes to return and from which origin number var request getBlockHashesFromNumberData if err := msg.Decode(&request); err != nil { return errResp(ErrDecode, "%v: %v", msg, err) } if request.Amount > uint64(downloader.MaxHashFetch) { request.Amount = uint64(downloader.MaxHashFetch) } // Calculate the last block that should be retrieved, and short circuit if unavailable last := pm.blockchain.GetBlockByNumber(request.Number + request.Amount - 1) if last == nil { last = pm.blockchain.CurrentBlock() request.Amount = last.NumberU64() - request.Number + 1 } if last.NumberU64() < request.Number { return p.SendBlockHashes(nil) } // Retrieve the hashes from the last block backwards, reverse and return hashes := []common.Hash{last.Hash()} hashes = append(hashes, pm.blockchain.GetBlockHashesFromHash(last.Hash(), request.Amount-1)...) for i := 0; i < len(hashes)/2; i++ { hashes[i], hashes[len(hashes)-1-i] = hashes[len(hashes)-1-i], hashes[i] } return p.SendBlockHashes(hashes) case p.version < eth62 && msg.Code == BlockHashesMsg: // A batch of hashes arrived to one of our previous requests var hashes []common.Hash if err := msg.Decode(&hashes); err != nil { break } // Deliver them all to the downloader for queuing err := pm.downloader.DeliverHashes(p.id, hashes) if err != nil { glog.V(logger.Debug).Infoln(err) } case p.version < eth62 && msg.Code == GetBlocksMsg: // Decode the retrieval message msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) if _, err := msgStream.List(); err != nil { return err } // Gather blocks until the fetch or network limits is reached var ( hash common.Hash bytes common.StorageSize blocks []*types.Block ) for len(blocks) < downloader.MaxBlockFetch && bytes < softResponseLimit { //Retrieve the hash of the next block err := msgStream.Decode(&hash) if err == rlp.EOL { break } else if err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } // Retrieve the requested block, stopping if enough was found if block := pm.blockchain.GetBlock(hash); block != nil { blocks = append(blocks, block) bytes += block.Size() } } return p.SendBlocks(blocks) case p.version < eth62 && msg.Code == BlocksMsg: // Decode the arrived block message var blocks []*types.Block if err := msg.Decode(&blocks); err != nil { glog.V(logger.Detail).Infoln("Decode error", err) blocks = nil } // Update the receive timestamp of each block for _, block := range blocks { block.ReceivedAt = msg.ReceivedAt block.ReceivedFrom = p } // Filter out any explicitly requested blocks, deliver the rest to the downloader if blocks := pm.fetcher.FilterBlocks(blocks); len(blocks) > 0 { pm.downloader.DeliverBlocks(p.id, blocks) } // Block header query, collect the requested headers and reply case p.version >= eth62 && msg.Code == GetBlockHeadersMsg: // Decode the complex header query var query getBlockHeadersData if err := msg.Decode(&query); err != nil { return errResp(ErrDecode, "%v: %v", msg, err) } hashMode := query.Origin.Hash != (common.Hash{}) // Gather headers until the fetch or network limits is reached var ( bytes common.StorageSize headers []*types.Header unknown bool ) for !unknown && len(headers) < int(query.Amount) && bytes < softResponseLimit && len(headers) < downloader.MaxHeaderFetch { // Retrieve the next header satisfying the query var origin *types.Header if hashMode { origin = pm.blockchain.GetHeader(query.Origin.Hash) } else { origin = pm.blockchain.GetHeaderByNumber(query.Origin.Number) } if origin == nil { break } headers = append(headers, origin) bytes += estHeaderRlpSize // Advance to the next header of the query switch { case query.Origin.Hash != (common.Hash{}) && query.Reverse: // Hash based traversal towards the genesis block for i := 0; i < int(query.Skip)+1; i++ { if header := pm.blockchain.GetHeader(query.Origin.Hash); header != nil { query.Origin.Hash = header.ParentHash } else { unknown = true break } } case query.Origin.Hash != (common.Hash{}) && !query.Reverse: // Hash based traversal towards the leaf block if header := pm.blockchain.GetHeaderByNumber(origin.Number.Uint64() + query.Skip + 1); header != nil { if pm.blockchain.GetBlockHashesFromHash(header.Hash(), query.Skip+1)[query.Skip] == query.Origin.Hash { query.Origin.Hash = header.Hash() } else { unknown = true } } else { unknown = true } case query.Reverse: // Number based traversal towards the genesis block if query.Origin.Number >= query.Skip+1 { query.Origin.Number -= (query.Skip + 1) } else { unknown = true } case !query.Reverse: // Number based traversal towards the leaf block query.Origin.Number += (query.Skip + 1) } } return p.SendBlockHeaders(headers) case p.version >= eth62 && msg.Code == BlockHeadersMsg: // A batch of headers arrived to one of our previous requests var headers []*types.Header if err := msg.Decode(&headers); err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } // Filter out any explicitly requested headers, deliver the rest to the downloader filter := len(headers) == 1 if filter { headers = pm.fetcher.FilterHeaders(headers, time.Now()) } if len(headers) > 0 || !filter { err := pm.downloader.DeliverHeaders(p.id, headers) if err != nil { glog.V(logger.Debug).Infoln(err) } } case p.version >= eth62 && msg.Code == GetBlockBodiesMsg: // Decode the retrieval message msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) if _, err := msgStream.List(); err != nil { return err } // Gather blocks until the fetch or network limits is reached var ( hash common.Hash bytes int bodies []rlp.RawValue ) for bytes < softResponseLimit && len(bodies) < downloader.MaxBlockFetch { // Retrieve the hash of the next block if err := msgStream.Decode(&hash); err == rlp.EOL { break } else if err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } // Retrieve the requested block body, stopping if enough was found if data := pm.blockchain.GetBodyRLP(hash); len(data) != 0 { bodies = append(bodies, data) bytes += len(data) } } return p.SendBlockBodiesRLP(bodies) case p.version >= eth62 && msg.Code == BlockBodiesMsg: // A batch of block bodies arrived to one of our previous requests var request blockBodiesData if err := msg.Decode(&request); err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } // Deliver them all to the downloader for queuing trasactions := make([][]*types.Transaction, len(request)) uncles := make([][]*types.Header, len(request)) for i, body := range request { trasactions[i] = body.Transactions uncles[i] = body.Uncles } // Filter out any explicitly requested bodies, deliver the rest to the downloader filter := len(trasactions) > 0 || len(uncles) > 0 if filter { trasactions, uncles = pm.fetcher.FilterBodies(trasactions, uncles, time.Now()) } if len(trasactions) > 0 || len(uncles) > 0 || !filter { err := pm.downloader.DeliverBodies(p.id, trasactions, uncles) if err != nil { glog.V(logger.Debug).Infoln(err) } } case p.version >= eth63 && msg.Code == GetNodeDataMsg: // Decode the retrieval message msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) if _, err := msgStream.List(); err != nil { return err } // Gather state data until the fetch or network limits is reached var ( hash common.Hash bytes int data [][]byte ) for bytes < softResponseLimit && len(data) < downloader.MaxStateFetch { // Retrieve the hash of the next state entry if err := msgStream.Decode(&hash); err == rlp.EOL { break } else if err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } // Retrieve the requested state entry, stopping if enough was found if entry, err := pm.chaindb.Get(hash.Bytes()); err == nil { data = append(data, entry) bytes += len(entry) } } return p.SendNodeData(data) case p.version >= eth63 && msg.Code == NodeDataMsg: // A batch of node state data arrived to one of our previous requests var data [][]byte if err := msg.Decode(&data); err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } // Deliver all to the downloader if err := pm.downloader.DeliverNodeData(p.id, data); err != nil { glog.V(logger.Debug).Infof("failed to deliver node state data: %v", err) } case p.version >= eth63 && msg.Code == GetReceiptsMsg: // Decode the retrieval message msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) if _, err := msgStream.List(); err != nil { return err } // Gather state data until the fetch or network limits is reached var ( hash common.Hash bytes int receipts []rlp.RawValue ) for bytes < softResponseLimit && len(receipts) < downloader.MaxReceiptFetch { // Retrieve the hash of the next block if err := msgStream.Decode(&hash); err == rlp.EOL { break } else if err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } // Retrieve the requested block's receipts, skipping if unknown to us results := core.GetBlockReceipts(pm.chaindb, hash) if results == nil { if header := pm.blockchain.GetHeader(hash); header == nil || header.ReceiptHash != types.EmptyRootHash { continue } } // If known, encode and queue for response packet if encoded, err := rlp.EncodeToBytes(results); err != nil { glog.V(logger.Error).Infof("failed to encode receipt: %v", err) } else { receipts = append(receipts, encoded) bytes += len(encoded) } } return p.SendReceiptsRLP(receipts) case p.version >= eth63 && msg.Code == ReceiptsMsg: // A batch of receipts arrived to one of our previous requests var receipts [][]*types.Receipt if err := msg.Decode(&receipts); err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } // Deliver all to the downloader if err := pm.downloader.DeliverReceipts(p.id, receipts); err != nil { glog.V(logger.Debug).Infof("failed to deliver receipts: %v", err) } case msg.Code == NewBlockHashesMsg: // Retrieve and deserialize the remote new block hashes notification type announce struct { Hash common.Hash Number uint64 } var announces = []announce{} if p.version < eth62 { // We're running the old protocol, make block number unknown (0) var hashes []common.Hash if err := msg.Decode(&hashes); err != nil { return errResp(ErrDecode, "%v: %v", msg, err) } for _, hash := range hashes { announces = append(announces, announce{hash, 0}) } } else { // Otherwise extract both block hash and number var request newBlockHashesData if err := msg.Decode(&request); err != nil { return errResp(ErrDecode, "%v: %v", msg, err) } for _, block := range request { announces = append(announces, announce{block.Hash, block.Number}) } } // Mark the hashes as present at the remote node for _, block := range announces { p.MarkBlock(block.Hash) p.SetHead(block.Hash) } // Schedule all the unknown hashes for retrieval unknown := make([]announce, 0, len(announces)) for _, block := range announces { if !pm.blockchain.HasBlock(block.Hash) { unknown = append(unknown, block) } } for _, block := range unknown { if p.version < eth62 { pm.fetcher.Notify(p.id, block.Hash, block.Number, time.Now(), p.RequestBlocks, nil, nil) } else { pm.fetcher.Notify(p.id, block.Hash, block.Number, time.Now(), nil, p.RequestOneHeader, p.RequestBodies) } } case msg.Code == NewBlockMsg: // Retrieve and decode the propagated block var request newBlockData if err := msg.Decode(&request); err != nil { return errResp(ErrDecode, "%v: %v", msg, err) } if err := request.Block.ValidateFields(); err != nil { return errResp(ErrDecode, "block validation %v: %v", msg, err) } request.Block.ReceivedAt = msg.ReceivedAt request.Block.ReceivedFrom = p // Mark the peer as owning the block and schedule it for import p.MarkBlock(request.Block.Hash()) p.SetHead(request.Block.Hash()) pm.fetcher.Enqueue(p.id, request.Block) // Update the peers total difficulty if needed, schedule a download if gapped if request.TD.Cmp(p.Td()) > 0 { p.SetTd(request.TD) td := pm.blockchain.GetTd(pm.blockchain.CurrentBlock().Hash()) if request.TD.Cmp(new(big.Int).Add(td, request.Block.Difficulty())) > 0 { go pm.synchronise(p) } } case msg.Code == TxMsg: // Transactions arrived, make sure we have a valid and fresh chain to handle them if atomic.LoadUint32(&pm.synced) == 0 { break } // Transactions can be processed, parse all of them and deliver to the pool var txs []*types.Transaction if err := msg.Decode(&txs); err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } for i, tx := range txs { // Validate and mark the remote transaction if tx == nil { return errResp(ErrDecode, "transaction %d is nil", i) } p.MarkTransaction(tx.Hash()) } pm.txpool.AddTransactions(txs) default: return errResp(ErrInvalidMsgCode, "%v", msg.Code) } return nil }
func (pm *ProtocolManager) handleMsg(p *peer) error { msg, err := p.rw.ReadMsg() if err != nil { return err } if msg.Size > ProtocolMaxMsgSize { return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) } // make sure that the payload has been fully consumed defer msg.Discard() switch msg.Code { case StatusMsg: return errResp(ErrExtraStatusMsg, "uncontrolled status message") case TxMsg: // TODO: rework using lazy RLP stream var txs []*types.Transaction if err := msg.Decode(&txs); err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } for i, tx := range txs { if tx == nil { return errResp(ErrDecode, "transaction %d is nil", i) } jsonlogger.LogJson(&logger.EthTxReceived{ TxHash: tx.Hash().Hex(), RemoteId: p.ID().String(), }) } pm.txpool.AddTransactions(txs) case GetBlockHashesMsg: var request getBlockHashesMsgData if err := msg.Decode(&request); err != nil { return errResp(ErrDecode, "->msg %v: %v", msg, err) } if request.Amount > uint64(downloader.MaxHashFetch) { request.Amount = uint64(downloader.MaxHashFetch) } hashes := pm.chainman.GetBlockHashesFromHash(request.Hash, request.Amount) if glog.V(logger.Debug) { if len(hashes) == 0 { glog.Infof("invalid block hash %x", request.Hash.Bytes()[:4]) } } // returns either requested hashes or nothing (i.e. not found) return p.sendBlockHashes(hashes) case BlockHashesMsg: msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) var hashes []common.Hash if err := msgStream.Decode(&hashes); err != nil { break } err := pm.downloader.DeliverHashes(p.id, hashes) if err != nil { glog.V(logger.Debug).Infoln(err) } case GetBlocksMsg: // Decode the retrieval message msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) if _, err := msgStream.List(); err != nil { return err } // Gather blocks until the fetch or network limits is reached var ( hash common.Hash bytes common.StorageSize hashes []common.Hash blocks []*types.Block ) for { err := msgStream.Decode(&hash) if err == rlp.EOL { break } else if err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } hashes = append(hashes, hash) // Retrieve the requested block, stopping if enough was found if block := pm.chainman.GetBlock(hash); block != nil { blocks = append(blocks, block) bytes += block.Size() if len(blocks) >= downloader.MaxBlockFetch || bytes > maxBlockRespSize { break } } } if glog.V(logger.Detail) && len(blocks) == 0 && len(hashes) > 0 { list := "[" for _, hash := range hashes { list += fmt.Sprintf("%x, ", hash[:4]) } list = list[:len(list)-2] + "]" glog.Infof("Peer %s: no blocks found for requested hashes %s", p.id, list) } return p.sendBlocks(blocks) case BlocksMsg: // Decode the arrived block message msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) var blocks []*types.Block if err := msgStream.Decode(&blocks); err != nil { glog.V(logger.Detail).Infoln("Decode error", err) blocks = nil } // Update the receive timestamp of each block for i := 0; i < len(blocks); i++ { blocks[i].ReceivedAt = msg.ReceivedAt } // Filter out any explicitly requested blocks, deliver the rest to the downloader if blocks := pm.fetcher.Filter(blocks); len(blocks) > 0 { pm.downloader.DeliverBlocks(p.id, blocks) } case NewBlockHashesMsg: // Retrieve and deseralize the remote new block hashes notification msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) var hashes []common.Hash if err := msgStream.Decode(&hashes); err != nil { break } // Mark the hashes as present at the remote node for _, hash := range hashes { p.blockHashes.Add(hash) p.SetHead(hash) } // Schedule all the unknown hashes for retrieval unknown := make([]common.Hash, 0, len(hashes)) for _, hash := range hashes { if !pm.chainman.HasBlock(hash) { unknown = append(unknown, hash) } } for _, hash := range unknown { pm.fetcher.Notify(p.id, hash, time.Now(), p.requestBlocks) } case NewBlockMsg: // Retrieve and decode the propagated block var request newBlockMsgData if err := msg.Decode(&request); err != nil { return errResp(ErrDecode, "%v: %v", msg, err) } if err := request.Block.ValidateFields(); err != nil { return errResp(ErrDecode, "block validation %v: %v", msg, err) } request.Block.ReceivedAt = msg.ReceivedAt // Mark the block's arrival for whatever reason _, chainHead, _ := pm.chainman.Status() jsonlogger.LogJson(&logger.EthChainReceivedNewBlock{ BlockHash: request.Block.Hash().Hex(), BlockNumber: request.Block.Number(), ChainHeadHash: chainHead.Hex(), BlockPrevHash: request.Block.ParentHash().Hex(), RemoteId: p.ID().String(), }) // Mark the peer as owning the block and schedule it for import p.blockHashes.Add(request.Block.Hash()) p.SetHead(request.Block.Hash()) pm.fetcher.Enqueue(p.id, request.Block) // TODO: Schedule a sync to cover potential gaps (this needs proto update) p.SetTd(request.TD) go pm.synchronise(p) default: return errResp(ErrInvalidMsgCode, "%v", msg.Code) } return nil }
func (self *ProtocolManager) handleMsg(p *peer) error { msg, err := p.rw.ReadMsg() if err != nil { return err } if msg.Size > ProtocolMaxMsgSize { return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) } // make sure that the payload has been fully consumed defer msg.Discard() switch msg.Code { case GetTxMsg: // ignore case StatusMsg: return errResp(ErrExtraStatusMsg, "uncontrolled status message") case TxMsg: // TODO: rework using lazy RLP stream var txs []*types.Transaction if err := msg.Decode(&txs); err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } for i, tx := range txs { if tx == nil { return errResp(ErrDecode, "transaction %d is nil", i) } jsonlogger.LogJson(&logger.EthTxReceived{ TxHash: tx.Hash().Hex(), RemoteId: p.ID().String(), }) } self.txpool.AddTransactions(txs) case GetBlockHashesMsg: var request getBlockHashesMsgData if err := msg.Decode(&request); err != nil { return errResp(ErrDecode, "->msg %v: %v", msg, err) } if request.Amount > maxHashes { request.Amount = maxHashes } hashes := self.chainman.GetBlockHashesFromHash(request.Hash, request.Amount) if glog.V(logger.Debug) { if len(hashes) == 0 { glog.Infof("invalid block hash %x", request.Hash.Bytes()[:4]) } } // returns either requested hashes or nothing (i.e. not found) return p.sendBlockHashes(hashes) case BlockHashesMsg: msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) var hashes []common.Hash if err := msgStream.Decode(&hashes); err != nil { break } err := self.downloader.AddHashes(p.id, hashes) if err != nil { glog.V(logger.Debug).Infoln(err) } case GetBlocksMsg: var blocks []*types.Block msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) if _, err := msgStream.List(); err != nil { return err } var i int for { i++ var hash common.Hash err := msgStream.Decode(&hash) if err == rlp.EOL { break } else if err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } block := self.chainman.GetBlock(hash) if block != nil { blocks = append(blocks, block) } if i == maxBlocks { break } } return p.sendBlocks(blocks) case BlocksMsg: var blocks []*types.Block msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) if err := msgStream.Decode(&blocks); err != nil { glog.V(logger.Detail).Infoln("Decode error", err) blocks = nil } self.downloader.DeliverChunk(p.id, blocks) case NewBlockMsg: var request newBlockMsgData if err := msg.Decode(&request); err != nil { return errResp(ErrDecode, "%v: %v", msg, err) } if err := request.Block.ValidateFields(); err != nil { return errResp(ErrDecode, "block validation %v: %v", msg, err) } request.Block.ReceivedAt = msg.ReceivedAt hash := request.Block.Hash() // Add the block hash as a known hash to the peer. This will later be used to determine // who should receive this. p.blockHashes.Add(hash) // update the peer info p.recentHash = hash p.td = request.TD _, chainHead, _ := self.chainman.Status() jsonlogger.LogJson(&logger.EthChainReceivedNewBlock{ BlockHash: hash.Hex(), BlockNumber: request.Block.Number(), // this surely must be zero ChainHeadHash: chainHead.Hex(), BlockPrevHash: request.Block.ParentHash().Hex(), RemoteId: p.ID().String(), }) // Make sure the block isn't already known. If this is the case simply drop // the message and move on. If the TD is < currentTd; drop it as well. If this // chain at some point becomes canonical, the downloader will fetch it. if self.chainman.HasBlock(hash) { break } if self.chainman.Td().Cmp(request.TD) > 0 && new(big.Int).Add(request.Block.Number(), big.NewInt(7)).Cmp(self.chainman.CurrentBlock().Number()) < 0 { glog.V(logger.Debug).Infof("[%s] dropped block %v due to low TD %v\n", p.id, request.Block.Number(), request.TD) break } // Attempt to insert the newly received by checking if the parent exists. // if the parent exists we process the block and propagate to our peers // otherwise synchronize with the peer if self.chainman.HasBlock(request.Block.ParentHash()) { if _, err := self.chainman.InsertChain(types.Blocks{request.Block}); err != nil { glog.V(logger.Error).Infoln("removed peer (", p.id, ") due to block error") self.removePeer(p) return nil } if err := self.verifyTd(p, request); err != nil { glog.V(logger.Error).Infoln(err) // XXX for now return nil so it won't disconnect (we should in the future) return nil } self.BroadcastBlock(hash, request.Block) } else { go self.synchronise(p) } default: return errResp(ErrInvalidMsgCode, "%v", msg.Code) } return nil }
// handleMsg is invoked whenever an inbound message is received from a remote // peer. The remote connection is torn down upon returning any error. func (pm *ProtocolManager) handleMsg(p *peer) error { // Read the next message from the remote peer, and ensure it's fully consumed msg, err := p.rw.ReadMsg() if err != nil { return err } if msg.Size > ProtocolMaxMsgSize { return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) } defer msg.Discard() // Handle the message depending on its contents switch msg.Code { case StatusMsg: // Status messages should never arrive after the handshake return errResp(ErrExtraStatusMsg, "uncontrolled status message") case GetBlockHashesMsg: // Retrieve the number of hashes to return and from which origin hash var request getBlockHashesData if err := msg.Decode(&request); err != nil { return errResp(ErrDecode, "%v: %v", msg, err) } if request.Amount > uint64(downloader.MaxHashFetch) { request.Amount = uint64(downloader.MaxHashFetch) } // Retrieve the hashes from the block chain and return them hashes := pm.chainman.GetBlockHashesFromHash(request.Hash, request.Amount) if len(hashes) == 0 { glog.V(logger.Debug).Infof("invalid block hash %x", request.Hash.Bytes()[:4]) } return p.SendBlockHashes(hashes) case GetBlockHashesFromNumberMsg: // Retrieve and decode the number of hashes to return and from which origin number var request getBlockHashesFromNumberData if err := msg.Decode(&request); err != nil { return errResp(ErrDecode, "%v: %v", msg, err) } if request.Amount > uint64(downloader.MaxHashFetch) { request.Amount = uint64(downloader.MaxHashFetch) } // Calculate the last block that should be retrieved, and short circuit if unavailable last := pm.chainman.GetBlockByNumber(request.Number + request.Amount - 1) if last == nil { last = pm.chainman.CurrentBlock() request.Amount = last.NumberU64() - request.Number + 1 } if last.NumberU64() < request.Number { return p.SendBlockHashes(nil) } // Retrieve the hashes from the last block backwards, reverse and return hashes := []common.Hash{last.Hash()} hashes = append(hashes, pm.chainman.GetBlockHashesFromHash(last.Hash(), request.Amount-1)...) for i := 0; i < len(hashes)/2; i++ { hashes[i], hashes[len(hashes)-1-i] = hashes[len(hashes)-1-i], hashes[i] } return p.SendBlockHashes(hashes) case BlockHashesMsg: // A batch of hashes arrived to one of our previous requests msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) reqHashInPacketsMeter.Mark(1) var hashes []common.Hash if err := msgStream.Decode(&hashes); err != nil { break } reqHashInTrafficMeter.Mark(int64(32 * len(hashes))) // Deliver them all to the downloader for queuing err := pm.downloader.DeliverHashes(p.id, hashes) if err != nil { glog.V(logger.Debug).Infoln(err) } case GetBlocksMsg: // Decode the retrieval message msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) if _, err := msgStream.List(); err != nil { return err } // Gather blocks until the fetch or network limits is reached var ( hash common.Hash bytes common.StorageSize hashes []common.Hash blocks []*types.Block ) for { err := msgStream.Decode(&hash) if err == rlp.EOL { break } else if err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } hashes = append(hashes, hash) // Retrieve the requested block, stopping if enough was found if block := pm.chainman.GetBlock(hash); block != nil { blocks = append(blocks, block) bytes += block.Size() if len(blocks) >= downloader.MaxBlockFetch || bytes > maxBlockRespSize { break } } } if glog.V(logger.Detail) && len(blocks) == 0 && len(hashes) > 0 { list := "[" for _, hash := range hashes { list += fmt.Sprintf("%x, ", hash[:4]) } list = list[:len(list)-2] + "]" glog.Infof("%v: no blocks found for requested hashes %s", p, list) } return p.SendBlocks(blocks) case BlocksMsg: // Decode the arrived block message msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) reqBlockInPacketsMeter.Mark(1) var blocks []*types.Block if err := msgStream.Decode(&blocks); err != nil { glog.V(logger.Detail).Infoln("Decode error", err) blocks = nil } // Update the receive timestamp of each block for _, block := range blocks { reqBlockInTrafficMeter.Mark(block.Size().Int64()) block.ReceivedAt = msg.ReceivedAt } // Filter out any explicitly requested blocks, deliver the rest to the downloader if blocks := pm.fetcher.Filter(blocks); len(blocks) > 0 { pm.downloader.DeliverBlocks(p.id, blocks) } case NewBlockHashesMsg: // Retrieve and deseralize the remote new block hashes notification msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) var hashes []common.Hash if err := msgStream.Decode(&hashes); err != nil { break } propHashInPacketsMeter.Mark(1) propHashInTrafficMeter.Mark(int64(32 * len(hashes))) // Mark the hashes as present at the remote node for _, hash := range hashes { p.MarkBlock(hash) p.SetHead(hash) } // Schedule all the unknown hashes for retrieval unknown := make([]common.Hash, 0, len(hashes)) for _, hash := range hashes { if !pm.chainman.HasBlock(hash) { unknown = append(unknown, hash) } } for _, hash := range unknown { pm.fetcher.Notify(p.id, hash, time.Now(), p.RequestBlocks) } case NewBlockMsg: // Retrieve and decode the propagated block var request newBlockData if err := msg.Decode(&request); err != nil { return errResp(ErrDecode, "%v: %v", msg, err) } propBlockInPacketsMeter.Mark(1) propBlockInTrafficMeter.Mark(request.Block.Size().Int64()) if err := request.Block.ValidateFields(); err != nil { return errResp(ErrDecode, "block validation %v: %v", msg, err) } request.Block.ReceivedAt = msg.ReceivedAt // Mark the block's arrival for whatever reason _, chainHead, _ := pm.chainman.Status() jsonlogger.LogJson(&logger.EthChainReceivedNewBlock{ BlockHash: request.Block.Hash().Hex(), BlockNumber: request.Block.Number(), ChainHeadHash: chainHead.Hex(), BlockPrevHash: request.Block.ParentHash().Hex(), RemoteId: p.ID().String(), }) // Mark the peer as owning the block and schedule it for import p.MarkBlock(request.Block.Hash()) p.SetHead(request.Block.Hash()) pm.fetcher.Enqueue(p.id, request.Block) // TODO: Schedule a sync to cover potential gaps (this needs proto update) if request.TD.Cmp(p.Td()) > 0 { p.SetTd(request.TD) go pm.synchronise(p) } case TxMsg: // Transactions arrived, parse all of them and deliver to the pool var txs []*types.Transaction if err := msg.Decode(&txs); err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } propTxnInPacketsMeter.Mark(1) for i, tx := range txs { // Validate and mark the remote transaction if tx == nil { return errResp(ErrDecode, "transaction %d is nil", i) } p.MarkTransaction(tx.Hash()) // Log it's arrival for later analysis propTxnInTrafficMeter.Mark(tx.Size().Int64()) jsonlogger.LogJson(&logger.EthTxReceived{ TxHash: tx.Hash().Hex(), RemoteId: p.ID().String(), }) } pm.txpool.AddTransactions(txs) default: return errResp(ErrInvalidMsgCode, "%v", msg.Code) } return nil }