// 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 (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.ChainManager, 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 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) // Update the peers total difficulty if needed, schedule a download if gapped if request.TD.Cmp(p.Td()) > 0 { p.SetTd(request.TD) if request.TD.Cmp(new(big.Int).Add(pm.chainman.Td(), request.Block.Difficulty())) > 0 { 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 }