Advertisement
Guest User

Untitled

a guest
Apr 19th, 2015
228
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.11 KB | None | 0 0
  1. package scorex
  2.  
  3. import akka.actor.Actor
  4. import com.google.common.primitives.{Bytes, Longs}
  5. import controller.Controller
  6. import database.{PrunableBlockchainStorage, UnconfirmedTransactionsDatabaseImpl}
  7. import ntp.NTP
  8. import scorex.account.PrivateKeyAccount
  9. import scorex.block.{Block, BlockStub}
  10. import scorex.crypto.Crypto
  11. import scorex.transaction.Transaction
  12. import scorex.transaction.Transaction.ValidationResult
  13. import scorex.wallet.Wallet
  14. import settings.Settings
  15.  
  16. import scala.collection.JavaConversions._
  17. import scala.collection.concurrent.TrieMap
  18.  
  19. case object TryToGenerateBlock
  20.  
  21. class BlockGenerator extends Actor {
  22.  
  23. import scorex.BlockGenerator._
  24.  
  25. override def receive = {
  26. case TryToGenerateBlock =>
  27. if (!Controller.isUpToDate()) Controller.update()
  28.  
  29. //CHECK IF WE HAVE CONNECTIONS
  30. if (Controller.getStatus == Controller.STATUS_OKE
  31. || (Controller.getStatus == Controller.STATUS_NO_CONNECTIONS && Settings.offlineGeneration)) {
  32. val blocks = TrieMap[PrivateKeyAccount, BlockStub]()
  33.  
  34. //GENERATE NEW BLOCKS
  35. Wallet.privateKeyAccounts().foreach { account =>
  36. if (account.generatingBalance >= BigDecimal(1)) {
  37. //CHECK IF BLOCK FROM USER ALREADY EXISTS USE MAP ACCOUNT BLOCK EASY
  38. if (!blocks.containsKey(account)) {
  39. //GENERATE NEW BLOCK FOR USER
  40. blocks += account -> generateNextBlock(account, PrunableBlockchainStorage.lastBlock)
  41. }
  42. }
  43. }
  44.  
  45. blocks.exists { case (account, blockStub) =>
  46. if (blockStub.timestamp <= NTP.getTime) {
  47. val block = formBlock(blockStub, account)
  48. if (block.transactions.nonEmpty) {
  49. println("Non-empty block: " + block)
  50. }
  51. Controller.newBlockGenerated(block)
  52. } else false
  53. }
  54. }
  55. }
  56. }
  57.  
  58. object BlockGenerator {
  59. val RETARGET = 10
  60. val MIN_BALANCE = 1L
  61. val MAX_BALANCE = 10000000000L
  62. val MIN_BLOCK_TIME = 1 * 60
  63. val MAX_BLOCK_TIME = 5 * 60
  64.  
  65. def getNextBlockGeneratingBalance(block: Block) = {
  66. if (block.height().get % RETARGET == 0) {
  67. //GET FIRST BLOCK OF TARGET
  68. val firstBlock = (1 to RETARGET - 1).foldLeft(block) { case (bl, _) => bl.parent().get}
  69.  
  70. //CALCULATE THE GENERATING TIME FOR LAST 10 BLOCKS
  71. val generatingTime = block.timestamp - firstBlock.timestamp
  72.  
  73. //CALCULATE EXPECTED FORGING TIME
  74. val expectedGeneratingTime = getBlockTime(block.generatingBalance) * RETARGET * 1000
  75.  
  76. //CALCULATE MULTIPLIER
  77. val multiplier = expectedGeneratingTime / generatingTime.toDouble
  78.  
  79. //CALCULATE NEW GENERATING BALANCE
  80. val generatingBalance = (block.generatingBalance * multiplier).toLong
  81. minMaxBalance(generatingBalance)
  82. } else block.generatingBalance
  83. }
  84.  
  85. def getBaseTarget(generatingBalance: Long) = minMaxBalance(generatingBalance) * getBlockTime(generatingBalance)
  86.  
  87. def getBlockTime(generatingBalance: Long) = {
  88. val percentageOfTotal = minMaxBalance(generatingBalance) / MAX_BALANCE.toDouble
  89. (MIN_BLOCK_TIME + ((MAX_BLOCK_TIME - MIN_BLOCK_TIME) * (1 - percentageOfTotal))).toLong
  90. }
  91.  
  92. private[BlockGenerator] def generateNextBlock(account: PrivateKeyAccount, block: Block) = {
  93. require(account.generatingBalance > BigDecimal(0), "Zero generating balance in generateNextBlock")
  94.  
  95. val signature = calculateSignature(block, account)
  96. val hash = Crypto.sha256(signature)
  97. val hashValue = BigInt(1, hash)
  98.  
  99. //CALCULATE ACCOUNT TARGET
  100. val targetBytes = Array.fill(32)(Byte.MaxValue)
  101. val baseTarget = BigInt(getBaseTarget(getNextBlockGeneratingBalance(block)))
  102. //MULTIPLY TARGET BY USER BALANCE
  103. val target = BigInt(1, targetBytes) / baseTarget * account.generatingBalance.toBigInt()
  104.  
  105.  
  106. //CALCULATE GUESSES
  107. val guesses = hashValue / target + 1
  108.  
  109. //CALCULATE TIMESTAMP
  110. val timestampRaw = guesses * 1000 + block.timestamp
  111.  
  112. //CHECK IF NOT HIGHER THAN MAX LONG VALUE
  113. val timestamp = if (timestampRaw > Long.MaxValue) Long.MaxValue else timestampRaw.longValue()
  114.  
  115. val version = 1
  116. BlockStub(version, block.signature, timestamp, getNextBlockGeneratingBalance(block), account, signature)
  117. }
  118.  
  119. private def calculateSignature(solvingBlock: Block, account: PrivateKeyAccount) = {
  120. //WRITE PARENT GENERATOR SIGNATURE
  121. val generatorSignature = Bytes.ensureCapacity(solvingBlock.generatorSignature, Block.GENERATOR_SIGNATURE_LENGTH, 0)
  122.  
  123. //WRITE GENERATING BALANCE
  124. val baseTargetBytesRaw = Longs.toByteArray(getNextBlockGeneratingBalance(solvingBlock))
  125. val baseTargetBytes = Bytes.ensureCapacity(baseTargetBytesRaw, Block.GENERATING_BALANCE_LENGTH, 0)
  126.  
  127. //WRITE GENERATOR
  128. val generatorBytes = Bytes.ensureCapacity(account.publicKey, Block.GENERATOR_LENGTH, 0)
  129.  
  130. //CALC SIGNATURE OF NEWBLOCKHEADER
  131. Crypto.sign(account, Bytes.concat(generatorSignature, baseTargetBytes, generatorBytes))
  132. }
  133.  
  134. private def formBlock(stub: BlockStub, account: PrivateKeyAccount): Block = {
  135.  
  136. //ORDER TRANSACTIONS BY FEE PER BYTE
  137. val orderedTransactions = UnconfirmedTransactionsDatabaseImpl.getAll().sortBy(_.feePerByte)
  138.  
  139. /* warning: simplification here!
  140. QORA does break after first transaction matched conditions then repeat cycle
  141. (while orderedTransactions contains transactions to process)
  142. */
  143. val (_, transactions) = orderedTransactions.foldLeft((0, List[Transaction]())) {
  144. case ((totalBytes, filteredTxs), tx) =>
  145. if (tx.timestamp <= stub.timestamp && tx.deadline > stub.timestamp
  146. && tx.isValid() == ValidationResult.VALIDATE_OKE
  147. && totalBytes + tx.dataLength <= Block.MAX_TRANSACTION_BYTES) {
  148.  
  149. tx.process()
  150. (totalBytes + tx.dataLength, tx :: filteredTxs)
  151. } else (totalBytes, filteredTxs)
  152. }
  153.  
  154. val data = transactions.foldLeft(stub.generatorSignature) { case (bytes, tx) =>
  155. Bytes.concat(bytes, tx.signature);
  156. }
  157.  
  158. val transactionsSingature = Crypto.sign(account, data)
  159.  
  160. Block(stub, transactions, transactionsSingature)
  161. }
  162.  
  163. private def minMaxBalance(generatingBalance: Long) =
  164. if (generatingBalance < MIN_BALANCE) MIN_BALANCE
  165. else if (generatingBalance > MAX_BALANCE) MAX_BALANCE
  166. else generatingBalance
  167. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement