github.com/deroproject/derosuite@v2.1.6-1.0.20200307070847-0f2e589c7a2b+incompatible/p2p/chain_request.go (about)

     1  // Copyright 2017-2018 DERO Project. All rights reserved.
     2  // Use of this source code in any form is governed by RESEARCH license.
     3  // license can be found in the LICENSE file.
     4  // GPG: 0F39 E425 8C65 3947 702A  8234 08B2 0360 A03A 9DE8
     5  //
     6  //
     7  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
     8  // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     9  // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
    10  // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    11  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    12  // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    13  // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    14  // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
    15  // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    16  
    17  package p2p
    18  
    19  //import "fmt"
    20  //import "net"
    21  import "sync/atomic"
    22  import "time"
    23  
    24  //import "container/list"
    25  
    26  //import log "github.com/sirupsen/logrus"
    27  import "github.com/romana/rlog"
    28  import "github.com/vmihailenco/msgpack"
    29  
    30  //import "github.com/deroproject/derosuite/crypto"
    31  import "github.com/deroproject/derosuite/globals"
    32  
    33  //import "github.com/deroproject/derosuite/blockchain"
    34  
    35  //  we are sending a chain request, build a packet with our chain data
    36  // so as other side can respond with chain response
    37  func (connection *Connection) Send_ChainRequest() {
    38  	var request Chain_Request_Struct
    39  
    40  	fill_common(&request.Common) // fill common info
    41  	request.Command = V2_COMMAND_CHAIN_REQUEST
    42  
    43  	// send our blocks, first 10 blocks directly, then decreasing in powers of 2
    44  	start_point := chain.Load_TOPO_HEIGHT(nil)
    45  	for i := int64(0); i < start_point; {
    46  
    47  		blid, _ := chain.Load_Block_Topological_order_at_index(nil, start_point-i)
    48  		request.Block_list = append(request.Block_list, blid)
    49  		request.TopoHeights = append(request.TopoHeights, start_point-i)
    50  		rlog.Tracef(3, "Adding block to chain request h %d %s", i, blid)
    51  		switch {
    52  		case len(request.Block_list) < 10:
    53  			i++
    54  		default:
    55  			i = i * 2
    56  		}
    57  	}
    58  
    59  	// add genesis block at the end
    60  	request.Block_list = append(request.Block_list, globals.Config.Genesis_Block_Hash)
    61  	request.TopoHeights = append(request.TopoHeights, 0)
    62  
    63  	// serialize and send
    64  	serialized, err := msgpack.Marshal(&request)
    65  	if err != nil {
    66  		panic(err)
    67  	}
    68  
    69  	// queue command
    70  	command := Queued_Command{Command: V2_COMMAND_CHAIN_RESPONSE}
    71  	connection.Objects <- command
    72  
    73  	atomic.StoreInt64(&connection.LastObjectRequestTime, time.Now().Unix())
    74  	//connection.Lock()
    75  	//connection.Command_queue.PushBack(command) // queue command
    76  	connection.Send_Message_prelocked(serialized)
    77  	//connection.Unlock()
    78  
    79  	rlog.Tracef(2, "chain request  sent successfully %s", globals.CTXString(connection.logger))
    80  }
    81  
    82  // peer has requested chain
    83  func (connection *Connection) Handle_ChainRequest(buf []byte) {
    84  	var request Chain_Request_Struct
    85  	var response Chain_Response_Struct
    86  
    87  	err := msgpack.Unmarshal(buf, &request)
    88  	if err != nil {
    89  		rlog.Warnf("Error while decoding incoming chain request err %s %s", err, globals.CTXString(connection.logger))
    90  		connection.Exit()
    91  		return
    92  	}
    93  
    94  	//
    95  	if len(request.Block_list) < 1 { // malformed request ban peer
    96  		rlog.Warnf("malformed chain request  received, banning peer %+v %s", request, globals.CTXString(connection.logger))
    97  		connection.Exit()
    98  
    99  		return
   100  	}
   101  
   102  	if len(request.Block_list) != len(request.TopoHeights) {
   103  		rlog.Warnf("Peer chain request has %d block %d topos, therefore invalid", len(request.Block_list), len(request.TopoHeights))
   104  		connection.Exit()
   105  		return
   106  	}
   107  
   108  	if request.Block_list[len(request.Block_list)-1] != globals.Config.Genesis_Block_Hash {
   109  		rlog.Warnf("Peer's genesis block is different from our, so disconnect Actual %s Expected %s", request.Block_list[len(request.Block_list)-1], globals.Config.Genesis_Block_Hash)
   110  		connection.Exit()
   111  		return
   112  	}
   113  
   114  	rlog.Tracef(2, "chain request received %s", globals.CTXString(connection.logger))
   115  
   116  	// we must give user our version of the chain
   117  	start_height := int64(0)
   118          start_topoheight := int64(0)
   119  
   120  	for i := 0; i < len(request.Block_list); i++ { // find the common point in our chain ( the block is NOT orphan)
   121  
   122  		//connection.logger.Infof("Checking block for chain detection %d %s", i, request.Block_list[i])
   123  
   124  		if chain.Block_Exists(nil, request.Block_list[i]) && chain.Is_Block_Topological_order(nil, request.Block_list[i]) &&
   125  			request.TopoHeights[i] == chain.Load_Block_Topological_order(nil, request.Block_list[i]) {
   126  			start_height = chain.Load_Height_for_BL_ID(nil, request.Block_list[i])
   127                          start_topoheight =  chain.Load_Block_Topological_order(nil, request.Block_list[i])
   128  			rlog.Tracef(2, "Found common point in chain at hash %x height %d topoheight %d\n", request.Block_list[i], start_height, start_topoheight)
   129  			break
   130  		}
   131  	}
   132  
   133  	// we can serve maximum of 512 BLID = 16K KB
   134  	const MAX_BLOCKS = 512
   135  
   136  	// if everything is OK, we must respond with chain response
   137  	//connection.Send_TimedSync(false) // send it as response
   138  
   139  	for i := start_topoheight; i <= chain.Load_TOPO_HEIGHT(nil) && len(response.Block_list) <= MAX_BLOCKS; i++ {
   140  		hash, _ := chain.Load_Block_Topological_order_at_index(nil, i)
   141  		response.Block_list = append(response.Block_list, [32]byte(hash))
   142  	}
   143  
   144  	// we must also fill blocks for the  last top 10 heights, so client can sync faster to alt tips
   145  	top_height := chain.Get_Height()
   146  	counter := 0
   147  	for ; top_height > 0 && counter <= 10; top_height-- {
   148  		blocks := chain.Get_Blocks_At_Height(nil, top_height)
   149  		for i := range blocks {
   150  			response.TopBlocks = append([][32]byte{blocks[i]}, response.TopBlocks...) // blocks are ordered height wise
   151  		}
   152  		counter++
   153  	}
   154  
   155  	response.Start_height = start_height
   156  	response.Start_topoheight = start_topoheight
   157  	fill_common(&response.Common) // fill common info
   158  	response.Command = V2_COMMAND_CHAIN_RESPONSE
   159  
   160  	// serialize and send
   161  	serialized, err := msgpack.Marshal(&response)
   162  	if err != nil {
   163  		panic(err)
   164  	}
   165  
   166  	// we should add to queue that we are waiting for chain response
   167  	rlog.Tracef(2, "chain response sent due to incoming chain request  sent len response = %d %s", len(serialized), globals.CTXString(connection.logger))
   168  	connection.Send_Message(serialized)
   169  }