github.com/deroproject/derosuite@v2.1.6-1.0.20200307070847-0f2e589c7a2b+incompatible/blockchain/miner_block.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 blockchain
    18  
    19  import "fmt"
    20  import "time"
    21  import "sync"
    22  import "strings"
    23  import "math/rand"
    24  import "runtime/debug"
    25  
    26  //import "encoding/binary"
    27  import "golang.org/x/time/rate"
    28  
    29  //import "encoding/hex"
    30  
    31  import "github.com/romana/rlog"
    32  
    33  // this file creates the blobs which can be used to mine new blocks
    34  
    35  import "github.com/deroproject/derosuite/block"
    36  import "github.com/deroproject/derosuite/config"
    37  import "github.com/deroproject/derosuite/crypto"
    38  import "github.com/deroproject/derosuite/globals"
    39  import "github.com/deroproject/derosuite/address"
    40  
    41  //import "github.com/deroproject/derosuite/emission"
    42  import "github.com/deroproject/derosuite/transaction"
    43  
    44  //NOTE: this function is quite naughty due to chicken and egg problem
    45  // we cannot calculate reward until we known blocksize ( exactly upto byte size )
    46  // also note that we cannot calculate blocksize  until we know reward
    47  // how we do it
    48  // reward field is a varint, ( can be 8 bytes )
    49  // so max deviation can be 8 bytes, due to reward
    50  // so we do a bruterforce till the reward is obtained but lets try to KISS
    51  // the top hash over which to do mining now ( it should already be in the chain)
    52  // this is work in progress
    53  // TODO we need to rework fees algorithm, to improve its quality and lower fees
    54  func (chain *Blockchain) Create_new_miner_block(miner_address address.Address) (cbl *block.Complete_Block, bl block.Block) {
    55  	//chain.Lock()
    56  	//defer chain.Unlock()
    57  
    58  	cbl = &block.Complete_Block{}
    59  
    60  	dbtx, err := chain.store.BeginTX(false)
    61  	if err != nil {
    62  		logger.Warnf(" Error opening readonly TX, err %s", err)
    63  		return
    64  	}
    65  
    66  	defer dbtx.Rollback()
    67  
    68  	// let give is a chance to mempool to do house keeping
    69  	// we can skip house-keeping
    70          /*
    71  	func() {
    72  		if r := recover(); r != nil {
    73  			logger.Warnf("Mempool House Keeping triggered panic height = %d", chain.Get_Height())
    74  		}
    75  
    76  		// give mempool an oppurtunity to verify alt-chain tx, but only if they are not mined
    77  		// but only check for double spending
    78  		chain.Mempool.HouseKeeping(uint64(chain.Get_Height()), func(tx *transaction.Transaction) bool {
    79  			return chain.Verify_Transaction_NonCoinbase_DoubleSpend_Check(dbtx, tx)
    80  		})
    81  	}() */
    82  	
    83  
    84  	// first of lets find the tx fees collected by consuming txs from mempool
    85  	tx_hash_list_sorted := chain.Mempool.Mempool_List_TX_SortedInfo() // hash of all tx expected to be included within this block , sorted by fees
    86  	var tx_hash_list_included []crypto.Hash                           // these tx will be included ( due to  block size limit )
    87  
    88  	// use best 3 tips
    89  	tips := chain.SortTips(dbtx, chain.Load_TIPS_ATOMIC(dbtx))
    90  	for i := range tips {
    91  		if len(bl.Tips) >= 3 {
    92  			break
    93  		}
    94  		bl.Tips = append(bl.Tips, tips[i])
    95  	}
    96  	height := chain.Calculate_Height_At_Tips(dbtx, bl.Tips) // we are 1 higher than previous highest tip
    97  
    98  	sizeoftxs := uint64(0) // size of all non coinbase tx included within this block
    99  	//fees_collected := uint64(0)
   100  
   101  	rlog.Infof("Total tx in pool %d", len(tx_hash_list_sorted))
   102  
   103  	reachable_key_images := chain.BuildReachabilityKeyImages(dbtx, &bl) // this requires only bl.Tips
   104  
   105  	// select 10%  tx based on fees
   106  	// select 90%  tx randomly
   107  	// random selection helps us to easily reach 80 TPS
   108  	i := 0
   109  	for ; i < len(tx_hash_list_sorted); i++ {
   110  
   111  		if (sizeoftxs + tx_hash_list_sorted[i].Size) > (10*config.CRYPTONOTE_MAX_BLOCK_SIZE)/100 { // limit block to max possible
   112  			break
   113  		}
   114  
   115  		tx := chain.Mempool.Mempool_Get_TX(tx_hash_list_sorted[i].Hash)
   116  		if tx != nil {
   117  			// skip and delete any mempool tx
   118  			if chain.Verify_Transaction_NonCoinbase_DoubleSpend_Check(dbtx, tx) == false {
   119  				chain.Mempool.Mempool_Delete_TX(tx_hash_list_sorted[i].Hash)
   120  				continue
   121  			}
   122  
   123  			failed := false
   124  			for j := 0; j < len(tx.Vin); j++ {
   125  				if _, ok := reachable_key_images[tx.Vin[j].(transaction.Txin_to_key).K_image]; ok {
   126  					rlog.Warnf("TX already in history, but tx %s  is still in mempool HOW ?? skipping it", tx_hash_list_sorted[i].Hash)
   127  					failed = true
   128  					break
   129  				}
   130  			}
   131  			if failed {
   132  				continue
   133  			}
   134  
   135  			rlog.Tracef(1,"Adding Top  Sorted tx %s to Complete_Block current size %.2f KB max possible %.2f KB\n", tx_hash_list_sorted[i].Hash, float32(sizeoftxs+tx_hash_list_sorted[i].Size)/1024.0, float32(config.CRYPTONOTE_MAX_BLOCK_SIZE)/1024.0)
   136  			sizeoftxs += tx_hash_list_sorted[i].Size
   137  			cbl.Txs = append(cbl.Txs, tx)
   138  			tx_hash_list_included = append(tx_hash_list_included, tx_hash_list_sorted[i].Hash)
   139  		}
   140  	}
   141  	// any left over transactions, should be randomly selected
   142  	tx_hash_list_sorted = tx_hash_list_sorted[i:]
   143  
   144  	// do random shuffling, can we get away with len/2 random shuffling
   145  	rand.Shuffle(len(tx_hash_list_sorted), func(i, j int) {
   146  		tx_hash_list_sorted[i], tx_hash_list_sorted[j] = tx_hash_list_sorted[j], tx_hash_list_sorted[i]
   147  	})
   148  
   149  	// if we were crossing limit, transactions would be randomly selected
   150  	// otherwise they will sorted by fees
   151  
   152  	// now select as much as possible
   153  	for i := range tx_hash_list_sorted {
   154  		if (sizeoftxs + tx_hash_list_sorted[i].Size) > (config.CRYPTONOTE_MAX_BLOCK_SIZE) { // limit block to max possible
   155  			break
   156  		}
   157  
   158  		tx := chain.Mempool.Mempool_Get_TX(tx_hash_list_sorted[i].Hash)
   159  		if tx != nil {
   160  
   161  			// skip and delete any mempool tx
   162  			if chain.Verify_Transaction_NonCoinbase_DoubleSpend_Check(dbtx, tx) == false {
   163  				chain.Mempool.Mempool_Delete_TX(tx_hash_list_sorted[i].Hash)
   164  				continue
   165  			}
   166  			failed := false
   167  			for j := 0; j < len(tx.Vin); j++ {
   168  				if _, ok := reachable_key_images[tx.Vin[j].(transaction.Txin_to_key).K_image]; ok {
   169  					rlog.Warnf("TX already in history, but tx %s  is still in mempool HOW ?? skipping it", tx_hash_list_sorted[i].Hash)
   170  					failed = true
   171  					break
   172  				}
   173  			}
   174  			if failed {
   175  				continue
   176  			}
   177  
   178  			rlog.Tracef(1,"Adding Random tx %s to Complete_Block current size %.2f KB max possible %.2f KB\n", tx_hash_list_sorted[i].Hash, float32(sizeoftxs+tx_hash_list_sorted[i].Size)/1024.0, float32(config.CRYPTONOTE_MAX_BLOCK_SIZE)/1024.0)
   179  			sizeoftxs += tx_hash_list_sorted[i].Size
   180  			cbl.Txs = append(cbl.Txs, tx)
   181  			tx_hash_list_included = append(tx_hash_list_included, tx_hash_list_sorted[i].Hash)
   182  		}
   183  	}
   184  
   185  	// collect tx list + their fees
   186  
   187  	// now we have all major parts of block, assemble the block
   188  	bl.Major_Version = uint64(chain.Get_Current_Version_at_Height(height))
   189  	bl.Minor_Version = uint64(chain.Get_Ideal_Version_at_Height(height)) // This is used for hard fork voting,
   190  	bl.Timestamp = uint64(uint64(time.Now().UTC().Unix()))
   191  	bl.Miner_TX, err = Create_Miner_TX2(int64(bl.Major_Version), height, miner_address)
   192  	if err != nil {
   193  		logger.Warnf("Error while creating miner block, err %s", err)
   194  	}
   195  
   196  	//bl.Prev_Hash = top_hash
   197  	bl.Nonce = rand.New(globals.NewCryptoRandSource()).Uint32() // nonce can be changed by the template header
   198  
   199  	for i := range tx_hash_list_included {
   200  		bl.Tx_hashes = append(bl.Tx_hashes, tx_hash_list_included[i])
   201  	}
   202  	cbl.Bl = &bl
   203  
   204  	//logger.Infof("miner block %+v", bl)
   205  	return
   206  }
   207  
   208  // returns a new block template ready for mining
   209  // block template has the following format
   210  // miner block header in hex  +
   211  // miner tx in hex +
   212  // 2 bytes ( inhex 4 bytes for number of tx )
   213  // tx hashes that follow
   214  var cache_block block.Block
   215  var cache_block_mutex sync.Mutex
   216  func (chain *Blockchain) Create_new_block_template_mining(top_hash crypto.Hash, miner_address address.Address, reserve_space int) (bl block.Block, blockhashing_blob string, block_template_blob string, reserved_pos int) {
   217  
   218  	rlog.Debugf("Mining block will give reward to %s", miner_address)
   219      cache_block_mutex.Lock()
   220      defer cache_block_mutex.Unlock()
   221      if cache_block.Timestamp < (uint64(uint64(time.Now().UTC().Unix()))) ||  (cache_block.Timestamp > 0 && int64(cache_block.Miner_TX.Vin[0].(transaction.Txin_gen).Height) != chain.Get_Height()+1)  {
   222          _, bl = chain.Create_new_miner_block(miner_address)
   223          cache_block = bl // setup cache for 1 sec
   224      }else{
   225         var err error
   226         bl = cache_block
   227          bl.Miner_TX, err = Create_Miner_TX2(int64(bl.Major_Version), int64(bl.Miner_TX.Vin[0].(transaction.Txin_gen).Height), miner_address)
   228          if err != nil {
   229                  logger.Warnf("Error while creating miner block, err %s", err)
   230          }
   231      }
   232  	blockhashing_blob = fmt.Sprintf("%x", bl.GetBlockWork())
   233  
   234  	// block template is all the parts of the block in dismantled form
   235  	// first is the block header
   236  	// then comes the miner tx
   237  	// then comes all the tx headers
   238  	block_template_blob = fmt.Sprintf("%x", bl.Serialize())
   239  
   240  	// lets locate  extra nonce
   241  	pos := strings.Index(blockhashing_blob, "0000000000000000000000000000000000000000000000000000000000000000")
   242  
   243  	pos = pos / 2 // we searched in hexadecimal form but we need to give position in byte form
   244  	reserved_pos = pos
   245  
   246  	return
   247  }
   248  
   249  // rate limiter is deployed, in case RPC is exposed over internet
   250  // someone should not be just giving fake inputs and delay chain syncing
   251  var accept_limiter = rate.NewLimiter(1.0, 4) // 1 block per sec, burst of 4 blocks is okay
   252  
   253  var accept_lock = sync.Mutex{}
   254  
   255  var duplicate_check = map[crypto.Hash]bool{}
   256  var duplicate_height_check = map[uint64]bool{}
   257  
   258  // accept work given by us
   259  // we should verify that the transaction list supplied back by the miner exists in the mempool
   260  // otherwise the miner is trying to attack the network
   261  func (chain *Blockchain) Accept_new_block(block_template []byte, blockhashing_blob []byte) (blid crypto.Hash,result bool, err error) {
   262  
   263  	// check whether  we are in lowcpu mode, if yes reject the block
   264  	if globals.Arguments["--lowcpuram"].(bool) {
   265  		globals.Logger.Warnf("Mining is deactivated since daemon is running in low cpu mode, please check program options.")
   266  		return blid,false, fmt.Errorf("Please decativate lowcpuram mode")
   267  	}
   268  	if globals.Arguments["--sync-node"].(bool) {
   269  		globals.Logger.Warnf("Mining is deactivated since daemon is running with --sync-mode, please check program options.")
   270  		return blid,false, fmt.Errorf("Please deactivate --sync-node option before mining")
   271  	}
   272  
   273  	accept_lock.Lock()
   274  	defer accept_lock.Unlock()
   275  
   276  	cbl := &block.Complete_Block{}
   277  	bl := block.Block{}
   278  
   279  	//logger.Infof("Incoming block for accepting %x", block_template)
   280  	// safety so if anything wrong happens, verification fails
   281  	defer func() {
   282  		if r := recover(); r != nil {
   283  			logger.Warnf("Recovered while accepting new block, Stack trace below ")
   284  			logger.Warnf("Stack trace  \n%s", debug.Stack())
   285  			err = fmt.Errorf("Error while parsing block")
   286  		}
   287  	}()
   288  
   289  	err = bl.Deserialize(block_template)
   290  	if err != nil {
   291  		logger.Warnf("Error parsing submitted work block template err %s", err)
   292  		return 
   293  	}
   294  
   295  	length_of_block_header := len(bl.Serialize())
   296  	template_data := block_template[length_of_block_header:]
   297  
   298  	if len(blockhashing_blob) >= 2{
   299  	err = bl.CopyNonceFromBlockWork(blockhashing_blob)
   300  	if err != nil {
   301  		logger.Warnf("Submitted block has been rejected, since blockhashing_blob is invalid")
   302  		return
   303  	}
   304          }
   305  
   306  	if len(template_data) != 0 {
   307  		logger.Warnf("Extra bytes (%d) left over while accepting block from mining pool %x", len(template_data), template_data)
   308  	}
   309  
   310  	// if we reach here, everything looks ok
   311  	// try to craft a complete block by grabbing entire tx from the mempool
   312  	//logger.Debugf("Block parsed successfully")
   313  	
   314  	blid = bl.GetHash()
   315  	
   316          // if a duplicate block is being sent, reject the block
   317  	if _, ok := duplicate_check[bl.Miner_TX.GetHash()]; ok {
   318  		logger.Warnf("Block %s rejected by chain due to duplicate work.", bl.GetHash())
   319                  err = fmt.Errorf("Error duplicate work")
   320  		return
   321  	}
   322  
   323  	if _,ok := duplicate_height_check[bl.Miner_TX.Vin[0].(transaction.Txin_gen).Height];ok{
   324  		logger.Warnf("Block %s rejected by chain due to duplicate hwork.", bl.GetHash())
   325                  err = fmt.Errorf("Error duplicate work")
   326  		return
   327  	}
   328  
   329  	// lets build up the complete block
   330  
   331  	// collect tx list + their fees
   332  	var tx *transaction.Transaction
   333  	for i := range bl.Tx_hashes {
   334  		tx = chain.Mempool.Mempool_Get_TX(bl.Tx_hashes[i])
   335  		if tx != nil {
   336  			cbl.Txs = append(cbl.Txs, tx)
   337  		} else {
   338  			tx, err = chain.Load_TX_FROM_ID(nil, bl.Tx_hashes[i])
   339  			if err != nil {
   340  				logger.Warnf("Tx %s not found in pool or DB, rejecting submitted block", bl.Tx_hashes[i])
   341  				return
   342  			}
   343  			cbl.Txs = append(cbl.Txs, tx)
   344  		}
   345  	}
   346  	cbl.Bl = &bl // the block is now complete, lets try to add it to chain
   347  
   348  
   349  
   350  	if !accept_limiter.Allow() { // if rate limiter allows, then add block to chain
   351  		logger.Warnf("Block %s rejected by chain.", bl.GetHash())
   352  		return
   353  	}
   354  
   355  	err, result = chain.Add_Complete_Block(cbl)
   356  
   357  	if result {
   358  
   359  		duplicate_check[bl.Miner_TX.GetHash()] = true
   360  		duplicate_height_check[bl.Miner_TX.Vin[0].(transaction.Txin_gen).Height] = true
   361  
   362  		logger.Infof("Block %s successfully accepted at height %d, Notifying Network", bl.GetHash(), chain.Load_Height_for_BL_ID(nil, bl.GetHash()))
   363          cache_block_mutex.Lock()
   364          defer cache_block_mutex.Unlock()
   365          bl.Timestamp = 0  // expire cache block
   366  
   367  		if !chain.simulator { // if not in simulator mode, relay block to the chain
   368  			chain.P2P_Block_Relayer(cbl, 0) // lets relay the block to network
   369  		}
   370  	} else {
   371  		logger.Warnf("Block Rejected %s error %s", bl.GetHash(), err)
   372  	}
   373  	return
   374  }