You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

205 lines
6.2 KiB

// 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/arnaucode/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)
}
}