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