// Copyright 2017-2018 DERO Project. All rights reserved. // Use of this source code in any form is governed by RESEARCH license. // license can be found in the LICENSE file. // GPG: 0F39 E425 8C65 3947 702A 8234 08B2 0360 A03A 9DE8 // // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package p2p import "fmt" import "net" import "time" import "github.com/romana/rlog" import log "github.com/sirupsen/logrus" import "github.com/deroproject/derosuite/globals" // the connection starts with P2P handshake // send the hand shake func Send_Handshake(connection *Connection) { // first request support flags if connection.Exit { return } Send_SupportFlags_Command(connection) connection.Lock() // lets do handshake var d Node_Data var c CORE_DATA var data_header Levin_Data_Header var levin_header Levin_Header d.Network_UUID = globals.Config.Network_ID d.Peer_ID = (uint64)(time.Now().Unix()) c.Current_Height = chain.Get_Height() c.Cumulative_Difficulty = chain.Get_Difficulty() c.Top_Version = 6 // top block id from the genesis or other block c.Top_ID = chain.Get_Top_ID() ds, _ := d.Serialize() cs, _ := c.Serialize() data_header.Data = ds data_header.Data = append(data_header.Data, cs...) levin_data, _ := data_header.Serialize() levin_header.CB = uint64(len(levin_data)) levin_header.Command = P2P_COMMAND_HANDSHAKE levin_header.ReturnData = true levin_header.Flags = LEVIN_PACKET_REQUEST header_bytes, _ := levin_header.Serialize() connection.Conn.Write(header_bytes) connection.Conn.Write(levin_data) connection.Command_queue.PushBack(uint32(P2P_COMMAND_HANDSHAKE)) connection.Unlock() } // handle P2P_COMMAND_HANDSHAKE, // we must send a response // our response is a boost compatible response which is parseable by old cryptonote daemons func Handle_P2P_Handshake_Command(connection *Connection, i_command_header *Levin_Header, buf []byte) { connection.logger.Infof("Handshake request arrived, we must parse it") // extract peers from our list, and insert them into the response // max 250 peers can be send ( since we are aiming compatibility with old daemons) var reply Node_Data_Response //panic("Handle_P2P_Handshake needs to fixed and tested") reply.NodeData.Network_UUID = globals.Config.Network_ID reply.NodeData.Peer_ID = (uint64)(OUR_PEER_ID) reply.NodeData.Local_time = uint64(time.Now().Unix()) reply.CoreData.Current_Height = chain.Get_Height() reply.CoreData.Cumulative_Difficulty = chain.Get_Difficulty() reply.CoreData.Top_Version = 6 // top block id from the genesis or other block // this data is from the block chain // this data must be the top block that we see till now reply.CoreData.Top_ID = chain.Get_Top_ID() for i := 0; i < 250; i++ { reply.PeerArray = append(reply.PeerArray, Peer_Info{IP: net.IPv4(byte(i), byte(i), byte(i), byte(i)), Port: 0, ID: 0, LastSeen: 0}) } var o_command_header Levin_Header var o_data_header Levin_Data_Header o_data_header.Data, _ = reply.Serialize() data, _ := o_data_header.Serialize() // mark as containing 4 elements data[9] = 0x10 o_command_header.CB = uint64(len(data)) o_command_header.Command = P2P_COMMAND_HANDSHAKE o_command_header.ReturnData = false o_command_header.ReturnCode = 1 // send as good response o_command_header.Flags = LEVIN_PACKET_RESPONSE o_command_header_bytes, _ := o_command_header.Serialize() connection.Conn.Write(o_command_header_bytes) connection.Conn.Write(data) rlog.Tracef(4, "Sending handshake response\n") Handle_P2P_Handshake_Command_Response(connection, i_command_header, buf) // parse incoming response } /* handles response of our p2p command, parses data etc*/ func Handle_P2P_Handshake_Command_Response(connection *Connection, i_command_header *Levin_Header, buf []byte) { var reply Node_Data_Response // deserialize data header var i_data_header Levin_Data_Header // incoming data header err := i_data_header.DeSerialize(buf) if err != nil { connection.logger.WithFields(log.Fields{ "ip": connection.Addr.IP, }).Debugf("Disconnecting client, handshake could not be deserialized") return } if reply.DeSerialize(i_data_header.Data) != nil { logger.WithFields(log.Fields{ "ip": connection.Addr.IP, }).Debugf("Disconnecting client, handshake could not be deserialized") return } if reply.NodeData.Network_UUID != globals.Config.Network_ID { logger.WithFields(log.Fields{ "ip": connection.Addr.IP, "id": reply.NodeData.Network_UUID, }).Debugf("Disconnecting client, Wrong network ID") return } // we need to kick the peer if the height is something specific and peer id is less than ours // TODO right we are not doing it connection.Peer_ID = reply.NodeData.Peer_ID connection.Port = reply.NodeData.Local_Port connection.Last_Height = reply.CoreData.Current_Height connection.Top_Version = uint64(reply.CoreData.Top_Version) connection.Top_ID = reply.CoreData.Top_ID connection.Cumulative_Difficulty = reply.CoreData.Cumulative_Difficulty connection.State = ACTIVE connection.logger.WithFields(log.Fields{ "PeerHeight": reply.CoreData.Current_Height, "Top Version": reply.CoreData.Top_Version, "Top_ID": fmt.Sprintf("%x", reply.CoreData.Top_ID), }).Debugf("Successful Handshake with Peer") // lets check whether we need to resync with this peer if chain.IsLagging(reply.CoreData.Cumulative_Difficulty, reply.CoreData.Current_Height, reply.CoreData.Top_ID) { logger.Debugf("We need to resync with the peer") // set mode to syncronising Send_BC_Notify_Chain_Command(connection) } }