github.com/deroproject/derosuite@v2.1.6-1.0.20200307070847-0f2e589c7a2b+incompatible/p2p/notification.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"
    22  import "time"
    23  
    24  import "encoding/binary"
    25  
    26  //import "container/list"
    27  
    28  import "github.com/romana/rlog"
    29  import "github.com/vmihailenco/msgpack"
    30  
    31  import "github.com/deroproject/derosuite/crypto"
    32  import "github.com/deroproject/derosuite/globals"
    33  import "github.com/deroproject/derosuite/block"
    34  import "github.com/deroproject/derosuite/errormsg"
    35  import "github.com/deroproject/derosuite/transaction"
    36  
    37  // Peer has notified us of a new transaction
    38  func (connection *Connection) Handle_Notification_Transaction(buf []byte) {
    39  	var request Notify_New_Objects_Struct
    40  
    41  	err := msgpack.Unmarshal(buf, &request)
    42  	if err != nil {
    43  		rlog.Warnf("Error while decoding incoming TX notifcation err %s %s", err, globals.CTXString(connection.logger))
    44  		connection.Exit()
    45  	}
    46  
    47  	var tx transaction.Transaction
    48  	err = tx.DeserializeHeader(request.Tx)
    49  	if err != nil { // we have a tx which could not be deserialized ban peer
    50  		rlog.Warnf("Error Incoming TX could not be deserialized err %s %s", err, globals.CTXString(connection.logger))
    51  		connection.Exit()
    52  		return
    53  	}
    54  
    55  	// track transaction propagation
    56  	if first_time, ok := tx_propagation_map.Load(tx.GetHash()); ok {
    57  		// block already has a reference, take the time and observe the value
    58  		diff := time.Now().Sub(first_time.(time.Time)).Round(time.Millisecond)
    59  		transaction_propagation.Observe(float64(diff / 1000000))
    60  	} else {
    61  		tx_propagation_map.Store(tx.GetHash(), time.Now()) // if this is the first time, store the tx time
    62  	}
    63  
    64  	// try adding tx to pool
    65  	success_pool := chain.Add_TX_To_Pool(&tx)
    66  
    67  	// add tx to cache  of the peer who sent us this tx
    68  	connection.TXpool_cache_lock.Lock()
    69  	if success_pool && globals.Arguments["--lowcpuram"].(bool) == false && connection.TXpool_cache != nil {
    70  
    71  		txhash := tx.GetHash()
    72  		connection.TXpool_cache[binary.LittleEndian.Uint64(txhash[:])] = uint32(time.Now().Unix())
    73  
    74  		//logger.Debugf("Adding %s to cache", tx.GetHash())
    75  	}
    76  	connection.TXpool_cache_lock.Unlock()
    77  
    78  	// broadcasting of tx is controlled by mempool
    79  
    80  }
    81  
    82  // Peer has notified us of a new block
    83  func (connection *Connection) Handle_Notification_Block(buf []byte) {
    84  	var request Notify_New_Objects_Struct
    85  
    86  	err := msgpack.Unmarshal(buf, &request)
    87  	if err != nil {
    88  		rlog.Warnf("Error while decoding incoming Block notifcation request err %s %s", err, globals.CTXString(connection.logger))
    89  		connection.Exit()
    90  	}
    91  
    92  	var cbl block.Complete_Block // parse incoming block and deserialize it
    93  	var bl block.Block
    94  	// lets deserialize block first and see whether it is the requested object
    95  	cbl.Bl = &bl
    96  	err = bl.Deserialize(request.CBlock.Block)
    97  	if err != nil { // we have a block which could not be deserialized ban peer
    98  		rlog.Warnf("Error Incoming block could not be deserilised err %s %s", err, globals.CTXString(connection.logger))
    99  		connection.Exit()
   100  		return
   101  	}
   102  
   103  	blid := bl.GetHash()
   104  
   105  	rlog.Infof("Incoming block Notification hash %s %s ", blid, globals.CTXString(connection.logger))
   106  
   107  	// track block propagation
   108  	if first_time, ok := block_propagation_map.Load(blid); ok {
   109  		// block already has a reference, take the time and observe the value
   110  		diff := time.Now().Sub(first_time.(time.Time)).Round(time.Millisecond)
   111  		block_propagation.Observe(float64(diff / 1000000))
   112  	} else {
   113  		block_propagation_map.Store(blid, time.Now()) // if this is the first time, store the block
   114  	}
   115  
   116  	// object is already is in our chain, we need not relay it
   117  	if chain.Block_Exists(nil, blid) {
   118  		return
   119  	}
   120  
   121  	// the block is not in our db,  parse entire block, complete the txs and try to add it
   122  	if len(bl.Tx_hashes) == len(request.CBlock.Txs) {
   123  		connection.logger.Debugf("Received a complete block %s with %d transactions",blid, len(bl.Tx_hashes))
   124  		for j := range request.CBlock.Txs {
   125  			var tx transaction.Transaction
   126  			err = tx.DeserializeHeader(request.CBlock.Txs[j])
   127  			if err != nil { // we have a tx which could not be deserialized ban peer
   128  				rlog.Warnf("Error Incoming TX could not be deserialized err %s %s", err, globals.CTXString(connection.logger))
   129  				connection.Exit()
   130  				return
   131  			}
   132  			cbl.Txs = append(cbl.Txs, &tx)
   133  		}
   134  	} else { // the block is NOT complete, we consider it as an ultra compact block
   135  
   136  		connection.logger.Debugf("Received an ultra compact block %s, total %d contains %d skipped %d transactions", blid,len(bl.Tx_hashes), len(request.CBlock.Txs), len(bl.Tx_hashes)-len(request.CBlock.Txs))
   137  		for j := range request.CBlock.Txs {
   138  			var tx transaction.Transaction
   139  			err = tx.DeserializeHeader(request.CBlock.Txs[j])
   140  			if err != nil { // we have a tx which could not be deserialized ban peer
   141  				rlog.Warnf("Error Incoming TX could not be deserialized err %s %s", err, globals.CTXString(connection.logger))
   142  				connection.Exit()
   143  				return
   144  			}
   145  			chain.Add_TX_To_Pool(&tx) // add tx to pool
   146  		}
   147  
   148  		// lets build a complete block ( tx from db or mempool )
   149  		for i := range bl.Tx_hashes {
   150  			if tx, err := chain.Load_TX_FROM_ID(nil, bl.Tx_hashes[i]); err == nil {
   151  				cbl.Txs = append(cbl.Txs, tx) // tx is from disk
   152  			} else {
   153  				tx := chain.Mempool.Mempool_Get_TX(bl.Tx_hashes[i]) // tx is from mempool
   154  				if tx != nil {
   155  					cbl.Txs = append(cbl.Txs, tx)
   156  				} else {
   157  					// the tx mentioned in ultra compact block could not be found
   158  					// request a full block
   159  
   160  					connection.Send_ObjectRequest([]crypto.Hash{blid}, []crypto.Hash{})
   161  					logger.Debugf("Ultra compact block  %s missing TX %s, requesting full block", blid, bl.Tx_hashes[i])
   162  					return
   163  				}
   164  			}
   165  
   166  		}
   167  	}
   168  
   169  	// check if we can add ourselves to chain
   170  	if err, ok := chain.Add_Complete_Block(&cbl); ok { // if block addition was successfil
   171  		// notify all peers
   172  		Broadcast_Block(&cbl, connection.Peer_ID) // do not send back to the original peer
   173  
   174  	} else { // ban the peer for sometime
   175  		if err == errormsg.ErrInvalidPoW {
   176  			connection.logger.Warnf("This peer should be banned and terminated")
   177  			connection.Exit()
   178  		}
   179  	}
   180  
   181  }