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 }