// rpcRelayBlock is an RPC that accepts a block from a peer. // COMPATv0.5.1 func (cs *ConsensusSet) rpcRelayBlock(conn modules.PeerConn) error { err := cs.tg.Add() if err != nil { return err } defer cs.tg.Done() // Decode the block from the connection. var b types.Block err = encoding.ReadObject(conn, &b, types.BlockSizeLimit) if err != nil { return err } // Submit the block to the consensus set and broadcast it. err = cs.managedAcceptBlock(b) if err == errOrphan { // If the block is an orphan, try to find the parents. The block // received from the peer is discarded and will be downloaded again if // the parent is found. go func() { err := cs.gateway.RPC(conn.RPCAddr(), "SendBlocks", cs.managedReceiveBlocks) if err != nil { cs.log.Debugln("WARN: failed to get parents of orphan block:", err) } }() } if err != nil { return err } cs.managedBroadcastBlock(b) return nil }
// requestNodes is the calling end of the ShareNodes RPC. func (g *Gateway) requestNodes(conn modules.PeerConn) error { var nodes []modules.NetAddress if err := encoding.ReadObject(conn, &nodes, maxSharedNodes*modules.MaxEncodedNetAddressLength); err != nil { return err } g.mu.Lock() for _, node := range nodes { err := g.addNode(node) if err != nil && err != errNodeExists && err != errOurAddress { g.log.Printf("WARN: peer '%v' sent the invalid addr '%v'", conn.RPCAddr(), node) } } g.save() g.mu.Unlock() return nil }
// threadedHandleConn reads header data from a connection, then routes it to the // appropriate handler for further processing. func (g *Gateway) threadedHandleConn(conn modules.PeerConn) { defer conn.Close() if g.threads.Add() != nil { return } defer g.threads.Done() var id rpcID if err := encoding.ReadObject(conn, &id, 8); err != nil { return } // call registered handler for this ID g.mu.RLock() fn, ok := g.handlers[id] g.mu.RUnlock() if !ok { g.log.Debugf("WARN: incoming conn %v requested unknown RPC \"%v\"", conn.RPCAddr(), id) return } g.log.Debugf("INFO: incoming conn %v requested RPC \"%v\"", conn.RPCAddr(), id) // call fn err := fn(conn) // don't log benign errors if err == modules.ErrDuplicateTransactionSet || err == modules.ErrBlockKnown { err = nil } if err != nil { g.log.Debugf("WARN: incoming RPC \"%v\" from conn %v failed: %v", id, conn.RPCAddr(), err) } }
// requestNodes is the calling end of the ShareNodes RPC. func (g *Gateway) requestNodes(conn modules.PeerConn) error { conn.SetDeadline(time.Now().Add(connStdDeadline)) var nodes []modules.NetAddress if err := encoding.ReadObject(conn, &nodes, maxSharedNodes*modules.MaxEncodedNetAddressLength); err != nil { return err } g.mu.Lock() for _, node := range nodes { err := g.addNode(node) if err != nil && err != errNodeExists && err != errOurAddress { g.log.Printf("WARN: peer '%v' sent the invalid addr '%v'", conn.RPCAddr(), node) } } err := g.save() if err != nil { g.log.Println("WARN: failed to save nodelist after requesting nodes:", err) } g.mu.Unlock() return nil }
// threadedRPCRelayHeader is an RPC that accepts a block header from a peer. func (cs *ConsensusSet) threadedRPCRelayHeader(conn modules.PeerConn) error { err := cs.tg.Add() if err != nil { return err } wg := new(sync.WaitGroup) defer func() { go func() { wg.Wait() cs.tg.Done() }() }() // Decode the block header from the connection. var h types.BlockHeader err = encoding.ReadObject(conn, &h, types.BlockHeaderSize) if err != nil { return err } // Start verification inside of a bolt View tx. cs.mu.RLock() err = cs.db.View(func(tx *bolt.Tx) error { // Do some relatively inexpensive checks to validate the header return cs.validateHeader(boltTxWrapper{tx}, h) }) cs.mu.RUnlock() if err == errOrphan { // If the header is an orphan, try to find the parents. Call needs to // be made in a separate goroutine as execution requires calling an // exported gateway method - threadedRPCRelayHeader was likely called // from an exported gateway function. // // NOTE: In general this is bad design. Rather than recycling other // calls, the whole protocol should have been kept in a single RPC. // Because it is not, we have to do weird threading to prevent // deadlocks, and we also have to be concerned every time the code in // managedReceiveBlocks is adjusted. wg.Add(1) go func() { err := cs.gateway.RPC(conn.RPCAddr(), "SendBlocks", cs.managedReceiveBlocks) if err != nil { cs.log.Debugln("WARN: failed to get parents of orphan header:", err) } wg.Done() }() return nil } else if err != nil { return err } // If the header is valid and extends the heaviest chain, fetch the // corresponding block. Call needs to be made in a separate goroutine // because an exported call to the gateway is used, which is a deadlock // risk given that rpcRelayHeader is called from the gateway. // // NOTE: In general this is bad design. Rather than recycling other calls, // the whole protocol should have been kept in a single RPC. Because it is // not, we have to do weird threading to prevent deadlocks, and we also // have to be concerned every time the code in managedReceiveBlock is // adjusted. wg.Add(1) go func() { err = cs.gateway.RPC(conn.RPCAddr(), "SendBlk", cs.managedReceiveBlock(h.ID())) if err != nil { cs.log.Debugln("WARN: failed to get header's corresponding block:", err) } wg.Done() }() return nil }