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.

349 lines
10 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 "bytes"
import "encoding/binary"
import "github.com/romana/rlog"
import "github.com/arnaucode/derosuite/crypto"
import "github.com/arnaucode/derosuite/block"
// The peer triggers this it wants some blocks or txs
func Handle_BC_Notify_Request_GetObjects(connection *Connection,
i_command_header *Levin_Header, buf []byte) {
// deserialize data header
var i_data_header Levin_Data_Header // incoming data header
err := i_data_header.DeSerialize(buf)
if err != nil {
connection.logger.Debugf("We should destroy connection here, data header cnot deserialized")
connection.Exit = true
return
}
pos := bytes.Index(buf, []byte("\x06blocks\x0a")) // at this point to
if pos < 0 {
rlog.Tracef(4, "NOTIFY_REQUEST_GET_OBJECTS doesnot contains blocks. Disconnect peer \n")
} else { // we have some block ids, extract and serve them
tmp_slice := buf[pos+8:]
rlog.Tracef(4, " varint %x", tmp_slice[:8])
data_length, done := Decode_Boost_Varint(tmp_slice)
tmp_slice = tmp_slice[done:]
if data_length == 0 {
rlog.Tracef(4, "Peer says it does not have even genesis block, so disconnect")
connection.Exit = true
return
}
rlog.Tracef(4, "Data size %d", data_length)
if (data_length % 32) != 0 { // sanity check
rlog.Tracef(4, "We should destroy connection here, packet mismatch")
connection.Exit = true
return
}
rlog.Tracef(4, "Number of hashes %d tmp_slice %x \n", data_length/32, tmp_slice[:32])
var block_list []crypto.Hash
for i := uint64(0); i < data_length/32; i++ {
var bhash crypto.Hash
copy(bhash[:], tmp_slice[i*32:(i+1)*32])
block_list = append(block_list, bhash)
rlog.Tracef(4, "%2d hash %x\n", i, bhash[:])
}
// send each block independently
/*if len(block_list) == 1 {
Send_Single_Block_to_Peer(connection, block_list[0])
} else*/{ // we need to send al blocks in 1 go
Send_Blocks_to_Peer(connection, block_list)
}
}
// we must give user data
}
func boost_serialisation_block(hash crypto.Hash) []byte {
block_header := []byte{0x04, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x0a}
txs_header := []byte{0x03, 0x74, 0x78, 0x73, 0x8a}
bl, err := chain.Load_BL_FROM_ID(hash)
_ = err
if len(bl.Tx_hashes) >= 1 {
block_header[0] = 0x8
}
block_serialized := bl.Serialize()
// add a varint len
buf := make([]byte, 8, 8)
done := Encode_Boost_Varint(buf, uint64(len(block_serialized))) // encode length of buffer
buf = buf[:done]
block_header = append(block_header, buf...)
block_header = append(block_header, block_serialized...)
if len(bl.Tx_hashes) >= 1 {
block_header = append(block_header, txs_header...)
// add txs length
buf := make([]byte, 8, 8)
done := Encode_Boost_Varint(buf, uint64(len(bl.Tx_hashes))) // encode length of buffer
buf = buf[:done]
block_header = append(block_header, buf...)
for i := range bl.Tx_hashes {
tx, err := chain.Load_TX_FROM_ID(bl.Tx_hashes[i])
if err != nil {
rlog.Tracef(1, "ERR Cannot load tx from DB\n")
return block_header
}
tx_serialized := tx.Serialize()
buf := make([]byte, 8, 8)
done := Encode_Boost_Varint(buf, uint64(len(tx_serialized))) // encode length of buffer
buf = buf[:done]
block_header = append(block_header, buf...)
block_header = append(block_header, tx_serialized...)
}
}
return block_header
}
func Send_Blocks_to_Peer(connection *Connection, block_list []crypto.Hash) {
blocks_header := []byte{0x01, 0x11, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x08, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
0x73, 0x8c} // this is followed by a varint count of blocks
trailer := []byte{0x19, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62,
0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x05}
buf := make([]byte, 8, 8)
binary.LittleEndian.PutUint64(buf, chain.Get_Height())
trailer = append(trailer, buf...)
done := Encode_Boost_Varint(buf, uint64(len(block_list))) // encode length of buffer
buf = buf[:done]
result := append(blocks_header, buf...)
for i := range block_list {
block := boost_serialisation_block(block_list[i])
result = append(result, block...)
}
result = append(result, trailer...)
var o_command_header Levin_Header
o_command_header.CB = uint64(len(result))
o_command_header.Command = BC_NOTIFY_RESPONSE_GET_OBJECTS
o_command_header.ReturnData = false
o_command_header.Flags = LEVIN_PACKET_REQUEST
o_command_header_bytes, _ := o_command_header.Serialize()
connection.Conn.Write(o_command_header_bytes)
connection.Conn.Write(result)
}
func Send_Single_Block_to_Peer(connection *Connection, hash crypto.Hash) {
bl, err := chain.Load_BL_FROM_ID(hash)
if err == nil {
if len(bl.Tx_hashes) == 0 {
Send_Block_with_ZERO_TX(connection, bl)
} else {
Send_Block_with_TX(connection, bl)
}
}
}
/*
header
00009F94 01 11 01 01 01 01 02 01 01 08 06 62 6c 6f 63 6b ........ ...block
00009FA4 73 8c 04 08 05 62 6c 6f 63 6b 0a s....blo ck......
trailer
000173C4 19 63 75 72 72 65 6e 74 5f 62 nm.'...c urrent_b
000173D4 6c 6f 63 6b 63 68 61 69 6e 5f 68 65 69 67 68 74 lockchai n_height
000173E4 05 ec 04 00 00 00 00 00 00
*/
// if a block is with out TX, send it in this format
func Send_Block_with_ZERO_TX(connection *Connection, bl *block.Block) {
fmt.Printf("sending block with zero tx")
header := []byte{0x01, 0x11, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x08, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
0x73, 0x8c, 0x04, 0x04, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x0a}
trailer := []byte{0x19, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62,
0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x05}
buf := make([]byte, 8, 8)
binary.LittleEndian.PutUint64(buf, chain.Get_Height())
trailer = append(trailer, buf...)
block_serialized := bl.Serialize()
done := Encode_Boost_Varint(buf, uint64(len(block_serialized))) // encode length of buffer
buf = buf[:done]
header = append(header, buf...)
header = append(header, block_serialized...)
header = append(header, trailer...)
var o_command_header Levin_Header
o_command_header.CB = uint64(len(header))
o_command_header.Command = BC_NOTIFY_RESPONSE_GET_OBJECTS
o_command_header.ReturnData = false
o_command_header.Flags = LEVIN_PACKET_REQUEST
o_command_header_bytes, _ := o_command_header.Serialize()
connection.Conn.Write(o_command_header_bytes)
connection.Conn.Write(header)
}
// if a block is with TX, send it in this format
func Send_Block_with_TX(connection *Connection, bl *block.Block) {
panic("Sending block with TX not implmented\n")
}
// header from boost packet
//0060 00 00 00 01 11 01 01 01 01 02 01 [] 01 04 09 62 6c ..............bl
//0070 6f 63 6b 5f 69 64 73 0a [] 81 0a 9b a2 3e fe 50 5f ock_ids.....>.P_
// send the peer the blocks hash and transactions hash, that we need
// this function, splits the request and serves
// we split the request into 2 requests
func Send_BC_Notify_Request_GetObjects(connection *Connection, block_list []crypto.Hash, tx_list []crypto.Hash) {
connection.Lock()
txs_header_bytes := []byte{0x03, 't', 'x', 's', 0x0a}
blocks_header_bytes := []byte{0x06, 'b', 'l', 'o', 'c', 'k', 's', 0x0a}
// now append boost variant length, and then append all the hashes
// add your blocks here
// adding genesis block
//block_list = append(block_list,globals.Config.Genesis_Block_Hash)
/*for i := 0; i < 100;i++{
block_list = append(block_list,globals.Config.Genesis_Block_Hash)
}*/
// split block request in independent request , so the response comes independent
if len(block_list) > 0 {
rlog.Tracef(4, "Sending block request for %d blocks \n", len(block_list))
for i := range block_list {
buf := make([]byte, 8, 8)
done := Encode_Boost_Varint(buf, uint64(32)) // encode length of buffer
buf = buf[:done]
var o_command_header Levin_Header
var o_data_header Levin_Data_Header
o_data_header.Data = append(blocks_header_bytes, buf...)
// convert and append all hashes to bytes
o_data_header.Data = append(o_data_header.Data, block_list[i][:32]...)
o_data_bytes, _ := o_data_header.Serialize()
o_data_bytes[9] = 0x4
o_command_header.CB = uint64(len(o_data_bytes))
o_command_header.Command = BC_NOTIFY_REQUEST_GET_OBJECTS
o_command_header.ReturnData = false
o_command_header.Flags = LEVIN_PACKET_REQUEST
o_command_header_bytes, _ := o_command_header.Serialize()
connection.Conn.Write(o_command_header_bytes)
connection.Conn.Write(o_data_bytes)
}
}
if len(tx_list) > 0 {
rlog.Tracef(4, "Sending tx request for %d tx \n", len(tx_list))
buf := make([]byte, 8, 8)
done := Encode_Boost_Varint(buf, uint64(len(tx_list)*32)) // encode length of buffer
buf = buf[:done]
var o_command_header Levin_Header
var o_data_header Levin_Data_Header
o_data_header.Data = append(txs_header_bytes, buf...)
// convert and append all hashes to bytes
for _, hash := range tx_list {
o_data_header.Data = append(o_data_header.Data, hash[:32]...)
}
o_data_bytes, _ := o_data_header.Serialize()
o_data_bytes[9] = 0x4
o_command_header.CB = uint64(len(o_data_bytes))
o_command_header.Command = BC_NOTIFY_REQUEST_GET_OBJECTS
o_command_header.ReturnData = false
o_command_header.Flags = LEVIN_PACKET_REQUEST
o_command_header_bytes, _ := o_command_header.Serialize()
connection.Conn.Write(o_command_header_bytes)
connection.Conn.Write(o_data_bytes)
}
//fmt.Printf("len of command header %d\n", len(o_command_header_bytes))
//fmt.Printf("len of data header %d\n", len(o_data_bytes))
connection.Unlock()
}