Example #1
0
func (p *Peer) CatchupWithPeer(blockHash []byte) {
	if !p.catchingUp {
		// Make sure nobody else is catching up when you want to do this
		p.catchingUp = true
		msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{blockHash, uint64(100)})
		p.QueueMessage(msg)

		peerlogger.DebugDetailf("Requesting blockchain %x... from peer %s\n", p.ethereum.BlockChain().CurrentBlock.Hash()[:4], p.conn.RemoteAddr())

		msg = ethwire.NewMessage(ethwire.MsgGetTxsTy, []interface{}{})
		p.QueueMessage(msg)
	}
}
Example #2
0
func (p *Peer) Start() {
	peerHost, peerPort, _ := net.SplitHostPort(p.conn.LocalAddr().String())
	servHost, servPort, _ := net.SplitHostPort(p.conn.RemoteAddr().String())

	if p.inbound {
		p.host, p.port = packAddr(peerHost, peerPort)
	} else {
		p.host, p.port = packAddr(servHost, servPort)
	}

	err := p.pushHandshake()
	if err != nil {
		peerlogger.Debugln("Peer can't send outbound version ack", err)

		p.Stop()

		return
	}

	go p.HandleOutbound()
	// Run the inbound handler in a new goroutine
	go p.HandleInbound()
	// Run the general update handler
	go p.update()

	// Wait a few seconds for startup and then ask for an initial ping
	time.Sleep(2 * time.Second)
	p.writeMessage(ethwire.NewMessage(ethwire.MsgPingTy, ""))
	p.pingStartTime = time.Now()

}
Example #3
0
func (self *Peer) FetchBlocks() {
	blockPool := self.ethereum.blockPool

	hashes := blockPool.Take(100, self)
	if len(hashes) > 0 {
		self.QueueMessage(ethwire.NewMessage(ethwire.MsgGetBlocksTy, ethutil.ByteSliceToInterface(hashes)))
	}
}
Example #4
0
func (p *Peer) CatchupWithPeer() {
	if !p.catchingUp {
		p.catchingUp = true
		msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{p.ethereum.BlockManager.BlockChain().CurrentBlock.Hash(), uint64(50)})
		p.QueueMessage(msg)

		ethutil.Config.Log.Debugf("Requesting blockchain %x...\n", p.ethereum.BlockManager.BlockChain().CurrentBlock.Hash()[:4])
	}
}
Example #5
0
func (p *Peer) peersMessage() *ethwire.Msg {
	outPeers := make([]interface{}, len(p.ethereum.InOutPeers()))
	// Serialise each peer
	for i, peer := range p.ethereum.InOutPeers() {
		outPeers[i] = peer.RlpData()
	}

	// Return the message to the peer with the known list of connected clients
	return ethwire.NewMessage(ethwire.MsgPeersTy, outPeers)
}
Example #6
0
func (p *Peer) Stop() {
	if atomic.AddInt32(&p.disconnect, 1) != 1 {
		return
	}

	close(p.quit)
	if atomic.LoadInt32(&p.connected) != 0 {
		p.writeMessage(ethwire.NewMessage(ethwire.MsgDiscTy, ""))
		p.conn.Close()
	}
}
Example #7
0
func (p *Peer) pushHandshake() error {
	pubkey := p.ethereum.KeyManager().PublicKey()
	msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, []interface{}{
		uint32(ProtocolVersion), uint32(0), []byte(p.version), byte(p.caps), p.port, pubkey[1:],
		p.ethereum.BlockChain().TD.Uint64(), p.ethereum.BlockChain().CurrentBlock.Hash(),
	})

	p.QueueMessage(msg)

	return nil
}
Example #8
0
func (self *Peer) FetchHashes() {
	blockPool := self.ethereum.blockPool

	if self.td.Cmp(blockPool.td) >= 0 {
		peerlogger.Debugf("Requesting hashes from %x\n", self.lastReceivedHash)

		if !blockPool.HasLatestHash() {
			self.QueueMessage(ethwire.NewMessage(ethwire.MsgGetBlockHashesTy, []interface{}{self.lastReceivedHash, uint32(200)}))
		}
	}
}
Example #9
0
func (p *Peer) pushHandshake() error {
	data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
	pubkey := ethutil.NewValueFromBytes(data).Get(2).Bytes()

	msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, []interface{}{
		uint32(ProtocolVersion), uint32(0), p.Version, byte(p.caps), p.port, pubkey,
	})

	p.QueueMessage(msg)

	return nil
}
Example #10
0
func (p *Peer) peersMessage() *ethwire.Msg {
	outPeers := make([]interface{}, len(p.ethereum.InOutPeers()))
	// Serialise each peer
	for i, peer := range p.ethereum.InOutPeers() {
		// Don't return localhost as valid peer
		if !net.ParseIP(peer.conn.RemoteAddr().String()).IsLoopback() {
			outPeers[i] = peer.RlpData()
		}
	}

	// Return the message to the peer with the known list of connected clients
	return ethwire.NewMessage(ethwire.MsgPeersTy, outPeers)
}
Example #11
0
// Outbound message handler. Outbound messages are handled here
func (p *Peer) HandleOutbound() {
	// The ping timer. Makes sure that every 2 minutes a ping is send to the peer
	pingTimer := time.NewTicker(pingPongTimer)
	serviceTimer := time.NewTicker(5 * time.Minute)

out:
	for {
		select {
		// Main message queue. All outbound messages are processed through here
		case msg := <-p.outputQueue:
			p.writeMessage(msg)
			p.lastSend = time.Now()

		// Ping timer
		case <-pingTimer.C:
			/*
				timeSince := time.Since(time.Unix(p.lastPong, 0))
				if !p.pingStartTime.IsZero() && p.lastPong != 0 && timeSince > (pingPongTimer+30*time.Second) {
					peerlogger.Infof("Peer did not respond to latest pong fast enough, it took %s, disconnecting.\n", timeSince)
					p.Stop()
					return
				}
			*/
			p.writeMessage(ethwire.NewMessage(ethwire.MsgPingTy, ""))
			p.pingStartTime = time.Now()

		// Service timer takes care of peer broadcasting, transaction
		// posting or block posting
		case <-serviceTimer.C:
			if p.caps&CapPeerDiscTy > 0 {
				msg := p.peersMessage()
				p.ethereum.BroadcastMsg(msg)
			}

		case <-p.quit:
			// Break out of the for loop if a quit message is posted
			break out
		}
	}

clean:
	// This loop is for draining the output queue and anybody waiting for us
	for {
		select {
		case <-p.outputQueue:
			// TODO
		default:
			break clean
		}
	}
}
Example #12
0
func (p *Peer) Stop() {
	if atomic.AddInt32(&p.disconnect, 1) != 1 {
		return
	}

	close(p.quit)
	if atomic.LoadInt32(&p.connected) != 0 {
		p.writeMessage(ethwire.NewMessage(ethwire.MsgDiscTy, ""))
		p.conn.Close()
	}

	// Pre-emptively remove the peer; don't wait for reaping. We already know it's dead if we are here
	p.ethereum.RemovePeer(p)
}
Example #13
0
// Outbound message handler. Outbound messages are handled here
func (p *Peer) HandleOutbound() {
	// The ping timer. Makes sure that every 2 minutes a ping is send to the peer
	pingTimer := time.NewTicker(2 * time.Minute)
	serviceTimer := time.NewTicker(5 * time.Minute)

out:
	for {
		select {
		// Main message queue. All outbound messages are processed through here
		case msg := <-p.outputQueue:
			p.writeMessage(msg)

			p.lastSend = time.Now()

		// Ping timer sends a ping to the peer each 2 minutes
		case <-pingTimer.C:
			p.writeMessage(ethwire.NewMessage(ethwire.MsgPingTy, ""))

		// Service timer takes care of peer broadcasting, transaction
		// posting or block posting
		case <-serviceTimer.C:
			if p.caps&CapPeerDiscTy > 0 {
				msg := p.peersMessage()
				p.ethereum.BroadcastMsg(msg)
			}

		case <-p.quit:
			// Break out of the for loop if a quit message is posted
			break out
		}
	}

clean:
	// This loop is for draining the output queue and anybody waiting for us
	for {
		select {
		case <-p.outputQueue:
			// TODO
		default:
			break clean
		}
	}
}
Example #14
0
func (p *Peer) FindCommonParentBlock() {
	if p.catchingUp {
		return
	}

	p.catchingUp = true
	if p.blocksRequested == 0 {
		p.blocksRequested = 20
	}
	blocks := p.ethereum.BlockChain().GetChain(p.ethereum.BlockChain().CurrentBlock.Hash(), p.blocksRequested)

	var hashes []interface{}
	for _, block := range blocks {
		hashes = append(hashes, block.Hash())
	}

	msgInfo := append(hashes, uint64(len(hashes)))

	peerlogger.DebugDetailf("Asking for block from %x (%d total) from %s\n", p.ethereum.BlockChain().CurrentBlock.Hash(), len(hashes), p.conn.RemoteAddr().String())

	msg := ethwire.NewMessage(ethwire.MsgGetChainTy, msgInfo)
	p.QueueMessage(msg)
}
Example #15
0
// Inbound handler. Inbound messages are received here and passed to the appropriate methods
func (p *Peer) HandleInbound() {
	for atomic.LoadInt32(&p.disconnect) == 0 {

		// HMM?
		time.Sleep(50 * time.Millisecond)
		// Wait for a message from the peer
		msgs, err := ethwire.ReadMessages(p.conn)
		if err != nil {
			peerlogger.Debugln(err)
		}
		for _, msg := range msgs {
			peerlogger.DebugDetailf("(%v) => %v %v\n", p.conn.RemoteAddr(), msg.Type, msg.Data)

			switch msg.Type {
			case ethwire.MsgHandshakeTy:
				// Version message
				p.handleHandshake(msg)

				if p.caps.IsCap(CapPeerDiscTy) {
					p.QueueMessage(ethwire.NewMessage(ethwire.MsgGetPeersTy, ""))
				}

			case ethwire.MsgDiscTy:
				p.Stop()
				peerlogger.Infoln("Disconnect peer: ", DiscReason(msg.Data.Get(0).Uint()))
			case ethwire.MsgPingTy:
				// Respond back with pong
				p.QueueMessage(ethwire.NewMessage(ethwire.MsgPongTy, ""))
			case ethwire.MsgPongTy:
				// If we received a pong back from a peer we set the
				// last pong so the peer handler knows this peer is still
				// active.
				p.lastPong = time.Now().Unix()
				p.pingTime = time.Since(p.pingStartTime)
			case ethwire.MsgTxTy:
				// If the message was a transaction queue the transaction
				// in the TxPool where it will undergo validation and
				// processing when a new block is found
				for i := 0; i < msg.Data.Len(); i++ {
					tx := ethchain.NewTransactionFromValue(msg.Data.Get(i))
					p.ethereum.TxPool().QueueTransaction(tx)
				}
			case ethwire.MsgGetPeersTy:
				// Peer asked for list of connected peers
				p.pushPeers()
			case ethwire.MsgPeersTy:
				// Received a list of peers (probably because MsgGetPeersTy was send)
				data := msg.Data
				// Create new list of possible peers for the ethereum to process
				peers := make([]string, data.Len())
				// Parse each possible peer
				for i := 0; i < data.Len(); i++ {
					value := data.Get(i)
					peers[i] = unpackAddr(value.Get(0), value.Get(1).Uint())
				}

				// Connect to the list of peers
				p.ethereum.ProcessPeerList(peers)
			case ethwire.MsgGetTxsTy:
				// Get the current transactions of the pool
				txs := p.ethereum.TxPool().CurrentTransactions()
				// Get the RlpData values from the txs
				txsInterface := make([]interface{}, len(txs))
				for i, tx := range txs {
					txsInterface[i] = tx.RlpData()
				}
				// Broadcast it back to the peer
				p.QueueMessage(ethwire.NewMessage(ethwire.MsgTxTy, txsInterface))

			case ethwire.MsgGetBlockHashesTy:
				if msg.Data.Len() < 2 {
					peerlogger.Debugln("err: argument length invalid ", msg.Data.Len())
				}

				hash := msg.Data.Get(0).Bytes()
				amount := msg.Data.Get(1).Uint()

				hashes := p.ethereum.BlockChain().GetChainHashesFromHash(hash, amount)

				p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockHashesTy, ethutil.ByteSliceToInterface(hashes)))

			case ethwire.MsgGetBlocksTy:
				// Limit to max 300 blocks
				max := int(math.Min(float64(msg.Data.Len()), 300.0))
				var blocks []interface{}

				for i := 0; i < max; i++ {
					hash := msg.Data.Get(i).Bytes()
					block := p.ethereum.BlockChain().GetBlock(hash)
					if block != nil {
						blocks = append(blocks, block.Value().Raw())
					}
				}

				p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockTy, blocks))

			case ethwire.MsgBlockHashesTy:
				p.catchingUp = true

				blockPool := p.ethereum.blockPool

				foundCommonHash := false

				it := msg.Data.NewIterator()
				for it.Next() {
					hash := it.Value().Bytes()

					if blockPool.HasCommonHash(hash) {
						foundCommonHash = true

						break
					}

					blockPool.AddHash(hash)

					p.lastReceivedHash = hash

					p.lastBlockReceived = time.Now()
				}

				if foundCommonHash {
					p.FetchBlocks()
				} else {
					p.FetchHashes()
				}

			case ethwire.MsgBlockTy:
				p.catchingUp = true

				blockPool := p.ethereum.blockPool

				it := msg.Data.NewIterator()

				for it.Next() {
					block := ethchain.NewBlockFromRlpValue(it.Value())

					blockPool.SetBlock(block)

					p.lastBlockReceived = time.Now()
				}

				linked := blockPool.CheckLinkAndProcess(func(block *ethchain.Block) {
					p.ethereum.StateManager().Process(block, false)
				})

				if !linked {
					p.FetchBlocks()
				}
			}
		}
	}

	p.Stop()
}
Example #16
0
func (s *Ethereum) Broadcast(msgType ethwire.MsgType, data []interface{}) {
	msg := ethwire.NewMessage(msgType, data)
	s.BroadcastMsg(msg)
}
Example #17
0
// Inbound handler. Inbound messages are received here and passed to the appropriate methods
func (p *Peer) HandleInbound() {

	for atomic.LoadInt32(&p.disconnect) == 0 {
		// HMM?
		time.Sleep(500 * time.Millisecond)

		// Wait for a message from the peer
		msgs, err := ethwire.ReadMessages(p.conn)
		if err != nil {
			ethutil.Config.Log.Debugln(err)
		}
		for _, msg := range msgs {
			switch msg.Type {
			case ethwire.MsgHandshakeTy:
				// Version message
				p.handleHandshake(msg)

				if p.caps.IsCap(CapPeerDiscTy) {
					p.QueueMessage(ethwire.NewMessage(ethwire.MsgGetPeersTy, ""))
				}
			case ethwire.MsgDiscTy:
				p.Stop()
				ethutil.Config.Log.Infoln("Disconnect peer:", DiscReason(msg.Data.Get(0).Uint()))
			case ethwire.MsgPingTy:
				// Respond back with pong
				p.QueueMessage(ethwire.NewMessage(ethwire.MsgPongTy, ""))
			case ethwire.MsgPongTy:
				// If we received a pong back from a peer we set the
				// last pong so the peer handler knows this peer is still
				// active.
				p.lastPong = time.Now().Unix()
			case ethwire.MsgBlockTy:
				// Get all blocks and process them
				var block, lastBlock *ethchain.Block
				var err error
				for i := msg.Data.Len() - 1; i >= 0; i-- {
					block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i))
					err = p.ethereum.BlockManager.ProcessBlock(block)

					if err != nil {
						if ethutil.Config.Debug {
							ethutil.Config.Log.Infof("[PEER] Block %x failed\n", block.Hash())
							ethutil.Config.Log.Infof("[PEER] %v\n", err)
						}
						break
					} else {
						lastBlock = block
					}
				}

				if err != nil {
					// If the parent is unknown try to catch up with this peer
					if ethchain.IsParentErr(err) {
						ethutil.Config.Log.Infoln("Attempting to catch up")
						p.catchingUp = false
						p.CatchupWithPeer()
					} else if ethchain.IsValidationErr(err) {
						// TODO
					}
				} else {
					// XXX Do we want to catch up if there were errors?
					// If we're catching up, try to catch up further.
					if p.catchingUp && msg.Data.Len() > 1 {
						if ethutil.Config.Debug && lastBlock != nil {
							blockInfo := lastBlock.BlockInfo()
							ethutil.Config.Log.Infof("Synced to block height #%d %x %x\n", blockInfo.Number, lastBlock.Hash(), blockInfo.Hash)
						}
						p.catchingUp = false
						p.CatchupWithPeer()
					}
				}
			case ethwire.MsgTxTy:
				// If the message was a transaction queue the transaction
				// in the TxPool where it will undergo validation and
				// processing when a new block is found
				for i := 0; i < msg.Data.Len(); i++ {
					p.ethereum.TxPool.QueueTransaction(ethchain.NewTransactionFromData(msg.Data.Get(i).Encode()))
				}
			case ethwire.MsgGetPeersTy:
				// Flag this peer as a 'requested of new peers' this to
				// prevent malicious peers being forced.
				p.requestedPeerList = true
				// Peer asked for list of connected peers
				p.pushPeers()
			case ethwire.MsgPeersTy:
				// Received a list of peers (probably because MsgGetPeersTy was send)
				// Only act on message if we actually requested for a peers list
				//if p.requestedPeerList {
				data := msg.Data
				// Create new list of possible peers for the ethereum to process
				peers := make([]string, data.Len())
				// Parse each possible peer
				for i := 0; i < data.Len(); i++ {
					value := data.Get(i)
					peers[i] = unpackAddr(value.Get(0), value.Get(1).Uint())
				}

				// Connect to the list of peers
				p.ethereum.ProcessPeerList(peers)
				// Mark unrequested again
				p.requestedPeerList = false

				//}
			case ethwire.MsgGetChainTy:
				var parent *ethchain.Block
				// Length minus one since the very last element in the array is a count
				l := msg.Data.Len() - 1
				// Ignore empty get chains
				if l == 0 {
					break
				}

				// Amount of parents in the canonical chain
				//amountOfBlocks := msg.Data.Get(l).AsUint()
				amountOfBlocks := uint64(100)
				// Check each SHA block hash from the message and determine whether
				// the SHA is in the database
				for i := 0; i < l; i++ {
					if data := msg.Data.Get(i).Bytes(); p.ethereum.BlockManager.BlockChain().HasBlock(data) {
						parent = p.ethereum.BlockManager.BlockChain().GetBlock(data)
						break
					}
				}

				// If a parent is found send back a reply
				if parent != nil {
					chain := p.ethereum.BlockManager.BlockChain().GetChainFromHash(parent.Hash(), amountOfBlocks)
					p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockTy, chain))
				} else {
					// If no blocks are found we send back a reply with msg not in chain
					// and the last hash from get chain
					lastHash := msg.Data.Get(l - 1)
					//log.Printf("Sending not in chain with hash %x\n", lastHash.AsRaw())
					p.QueueMessage(ethwire.NewMessage(ethwire.MsgNotInChainTy, []interface{}{lastHash.Raw()}))
				}
			case ethwire.MsgNotInChainTy:
				ethutil.Config.Log.Infof("Not in chain %x\n", msg.Data)
				// TODO

				// Unofficial but fun nonetheless
			case ethwire.MsgTalkTy:
				ethutil.Config.Log.Infoln("%v says: %s\n", p.conn.RemoteAddr(), msg.Data.Str())
			}
		}
	}

	p.Stop()
}