// 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 p2pv2 import "net" import "encoding/binary" // This file defines the structure for the protocol which is a msgp encoded ( which is standard) // msgp would cause an easy rewrite of p2p layer even in c, ruby or rust etc as future may demand // the protocol is length prefixed msgp payload // though we can use http2 stream features, they may become compilcated as the project evolve // the prefix length is 4 bytes, little endian encoded ( so a frame can be 4GB in size) // this is Work-In-Progress // the reason for writing it from scratch is the mess of boost serialisation // the p2p package is currently the most complex within the entire project // the protocol is partly syncronous, partly asyncronous , except for first handshake, so the node remain undetectable to external network scans, the detection cost is to atleast send a handshake packet*/ // these are the commands required to make it completely operational const V2_COMMAND_HANDSHAKE = 1 // commands are syncronous and must be responded within 10 secs const V2_COMMAND_SYNC = 2 const V2_COMMAND_CHAIN_REQUEST = 3 const V2_COMMAND_CHAIN_RESPONSE = 4 const V2_COMMAND_OBJECTS_REQUEST = 5 const V2_COMMAND_OBJECTS_RESPONSE = 6 const V2_NOTIFY_NEW_BLOCK = 0x80000000 // Notifications are asyncronous all notifications come here, such as new block, new txs const V2_NOTIFY_NEW_TX = 0x80000001 // notify tx using this // used to parse incoming packet for for command , so as a repective command command could be triggered type Common struct { Command uint64 `msgpack:"C"` Height uint64 `msgpack:"H"` Cumulative_Difficulty uint64 `msgpack:"CD"` Top_ID [32]byte `msgpack:"TI"` // 32 bytes of Top block Top_Version uint64 `msgpack:"TV"` // this basically represents the hard fork version } // at start, client sends handshake and server will respond to handshake type Handshake struct { Common // add all fields of Common Local_Time int64 `msgpack:"LT"` Local_Port uint32 `msgpack:"LP"` PeerID uint64 `msgpack:"PID"` Network_ID [16]byte `msgpack:"NID"` // 16 bytes PeerList []Peer_Info `msgpack:"PLIST"` Extension_List []string `msgpack:"EXT"` Request bool `msgpack:"REQUEST"` //whether this is a request } type Peer_Info struct { IP net.IP `msgpack:"IP"` Port uint32 `msgpack:"P"` ID uint64 `msgpack:"I"` LastSeen uint64 `msgpack:"LS"` } type Sync struct { Common // add all fields of common } type Chain_Request struct { Block_list [][32]byte `msgpack:"BLIST"` } type Chain_Response struct { Start_height uint64 `msgpack:"SH"` Block_list [][32]byte `msgpack:"BLIST"` } type Object_Request struct { Block_list [][32]byte `msgpack:"BLIST"` Tx_list [][32]byte `msgpack:"TXLIST"` } type Complete_Block struct { Block []byte `msgpack:"BLOCK"` Txs [][]byte `msgpack:"TXS"` } type Object_Response struct { Blocks []Complete_Block `msgpack:"CBLOCKS"` Txs [][]byte `msgpack:"TXS"` } type Notify_New_Objects struct { Block Complete_Block `msgpack:"CBLOCK"` Txs [][]byte `msgpack:"TXS"` } // each packet has to be parsed twice once for extracting command and then a full parsing // the message is sent as follows // assumingin peer lock has already been taken func (conn *Connection) Send_Message(data_bytes []byte) { var length_bytes [4]byte binary.LittleEndian.PutUint32(length_bytes[:], uint32(len(data_bytes))) // send the length prefix conn.Conn.Write(length_bytes[:]) conn.Conn.Write(data_bytes) // send the message itself }