Advertisement
Guest User

Untitled

a guest
Dec 22nd, 2014
167
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.95 KB | None | 0 0
  1. package qora
  2.  
  3. import java.math.BigDecimal
  4. import java.math.BigInteger
  5. import ntp.NTP
  6. import qora.account.PrivateKeyAccount
  7. import qora.block.Block
  8. import qora.block.BlockFactory
  9. import qora.crypto.Crypto
  10. import qora.transaction.Transaction
  11. import com.google.common.primitives.Bytes
  12. import com.google.common.primitives.Longs
  13. import controller.Controller
  14. import database.DBSet
  15. import scala.collection.JavaConversions._
  16. import scala.collection.concurrent.TrieMap
  17.  
  18.  
  19. /**
  20. * Scala version of QORA's BlockGenerator
  21. *
  22. * There's one behavioral difference from original logic, please see comments to the
  23. * addUnconfirmedTransactions method. But it should be compatible with QORA.
  24. *
  25. *
  26. * kushti
  27. */
  28.  
  29. // TODO: make code more functional, i.e. get off of mutable variables, while(true) etc
  30.  
  31. object BlockGenerator extends Thread {
  32. val RETARGET = 10
  33. val MIN_BALANCE = 1L
  34. val MAX_BALANCE = 10000000000L
  35. val MIN_BLOCK_TIME = 1 * 60
  36. val MAX_BLOCK_TIME = 5 * 60
  37.  
  38. private val blocks = TrieMap[PrivateKeyAccount, Block]()
  39. private var solvingBlock: Block = _
  40.  
  41. def addUnconfirmedTransaction(transaction: Transaction): Unit =
  42. addUnconfirmedTransaction(DBSet.getInstance(), transaction)
  43.  
  44. def addUnconfirmedTransaction(db: DBSet, transaction: Transaction): Unit =
  45. db.getTransactionMap.add(transaction)
  46.  
  47. def getUnconfirmedTransactions = DBSet.getInstance().getTransactionMap.getValues.toSeq
  48.  
  49. private def getKnownAccounts = Controller.getInstance().getPrivateKeyAccounts
  50.  
  51. override def run() {
  52. while (true) {
  53. //CHECK IF WE ARE UPTODATE
  54. if (!Controller.getInstance().isUpToDate) {
  55. Controller.getInstance().update()
  56. }
  57.  
  58. //CHECK IF WE HAVE CONNECTIONS
  59. if (Controller.getInstance().getStatus == Controller.STATUS_OKE) {
  60. val lastBlockSignature = DBSet.getInstance().getBlockMap.getLastBlockSignature
  61.  
  62. //CHECK IF DIFFERENT FOR CURRENT SOLVING BLOCK
  63. if (this.solvingBlock == null || !this.solvingBlock.getSignature.sameElements(lastBlockSignature)) {
  64. //SET NEW BLOCK TO SOLVE
  65. this.solvingBlock = DBSet.getInstance().getBlockMap.getLastBlock
  66.  
  67. //RESET BLOCKS
  68. this.blocks.clear()
  69. }
  70.  
  71. //GENERATE NEW BLOCKS
  72. if (Controller.getInstance().doesWalletExists()) {
  73. this.getKnownAccounts foreach { account =>
  74.  
  75. if (account.getGeneratingBalance.compareTo(BigDecimal.ONE) >= 0) {
  76. //CHECK IF BLOCK FROM USER ALREADY EXISTS USE MAP ACCOUNT BLOCK EASY
  77. if (!this.blocks.containsKey(account)) {
  78. //GENERATE NEW BLOCK FOR USER
  79. blocks += account -> this.generateNextBlock(DBSet.getInstance(), account, this.solvingBlock)
  80. }
  81. }
  82. }
  83. }
  84.  
  85. //IS VALID BLOCK FOUND?
  86. val validBlockFound = this.blocks.keySet.foldLeft(false) {case (found, account) =>
  87. val block = this.blocks.get(account).get
  88. if (!found && block.getTimestamp <= NTP.getTime) {
  89. //ADD TRANSACTIONS
  90. this.addUnconfirmedTransactions(DBSet.getInstance(), block)
  91.  
  92. //ADD TRANSACTION SIGNATURE
  93. block.setTransactionsSignature(this.calculateTransactionsSignature(block, account))
  94.  
  95. //PASS BLOCK TO CONTROLLER
  96. Controller.getInstance().newBlockGenerated(block)
  97.  
  98. true
  99. } else false
  100. }
  101.  
  102. if (!validBlockFound) Thread.sleep(100)
  103. } else {
  104. Thread.sleep(100)
  105. }
  106. }
  107. }
  108.  
  109. def generateNextBlock(db: DBSet, account: PrivateKeyAccount, block: Block) = {
  110. //CHECK IF ACCOUNT HAS BALANCE - but already checked before call (kushti)
  111. require (account.getGeneratingBalance(db) != BigDecimal.ZERO, "Zero balance in generateNextBlock")
  112.  
  113. val signature = this.calculateSignature(db, block, account)
  114. val hash = Crypto.getInstance().digest(signature)
  115. val hashValue = new BigInteger(1, hash)
  116.  
  117. //CALCULATE ACCOUNT TARGET
  118. val targetBytes = Array.fill(32)(Byte.MaxValue)
  119. val baseTarget = BigInteger.valueOf(getBaseTarget(getNextBlockGeneratingBalance(db, block)))
  120. val target = new BigInteger(1, targetBytes)
  121. .divide(baseTarget)
  122. .multiply(account.getGeneratingBalance(db).toBigInteger) //MULTIPLY TARGET BY USER BALANCE
  123.  
  124.  
  125. //CALCULATE GUESSES
  126. val guesses = hashValue.divide(target).add(BigInteger.ONE)
  127.  
  128. //CALCULATE TIMESTAMP
  129. val timestampRaw = guesses.multiply(BigInteger.valueOf(1000)).add(BigInteger.valueOf(block.getTimestamp))
  130.  
  131. //CHECK IF NOT HIGHER THAN MAX LONG VALUE
  132. val timestamp = (if (timestampRaw.compareTo(BigInteger.valueOf(Long.MaxValue)) == 1)
  133. BigInteger.valueOf(Long.MaxValue)
  134. else timestampRaw).longValue()
  135.  
  136. val version = 1
  137. BlockFactory.getInstance().create(version, block.getSignature, timestamp, getNextBlockGeneratingBalance(db, block), account, signature)
  138. }
  139.  
  140. def calculateSignature(db: DBSet, solvingBlock: Block, account: PrivateKeyAccount) = {
  141. //WRITE PARENT GENERATOR SIGNATURE
  142. val generatorSignature = Bytes.ensureCapacity(solvingBlock.getGeneratorSignature, Block.GENERATOR_SIGNATURE_LENGTH, 0)
  143.  
  144. //WRITE GENERATING BALANCE
  145. val baseTargetBytesRaw = Longs.toByteArray(getNextBlockGeneratingBalance(db, solvingBlock))
  146. val baseTargetBytes = Bytes.ensureCapacity(baseTargetBytesRaw, Block.GENERATING_BALANCE_LENGTH, 0)
  147.  
  148. //WRITE GENERATOR
  149. val generatorBytes = Bytes.ensureCapacity(account.getPublicKey, Block.GENERATOR_LENGTH, 0)
  150.  
  151. //CALC SIGNATURE OF NEWBLOCKHEADER
  152. Crypto.getInstance().sign(account, Bytes.concat(generatorSignature, baseTargetBytes, generatorBytes))
  153. }
  154.  
  155. def calculateTransactionsSignature(block: Block, account: PrivateKeyAccount) = {
  156. val data = block.getTransactions.foldLeft(block.getGeneratorSignature) { case (bytes, tx) =>
  157. Bytes.concat(bytes, tx.getSignature);
  158. }
  159. Crypto.getInstance().sign(account, data)
  160. }
  161.  
  162. def addUnconfirmedTransactions(db: DBSet, block: Block) = {
  163. //CREATE FORK OF GIVEN DATABASE
  164. val newBlockDb = db.fork()
  165.  
  166. //ORDER TRANSACTIONS BY FEE PER BYTE
  167. val orderedTransactions = db.getTransactionMap.getValues.toSeq.sortBy(_.feePerByte())
  168.  
  169. /* warning: simplification here!
  170. QORA does break after first transaction matched conditions then repeat cycle
  171. (while orderedTransactions contains transactions to process)
  172. */
  173. orderedTransactions.foldLeft(0) { case (totalBytes, tx) =>
  174. if (tx.getTimestamp <= block.getTimestamp && tx.getDeadline > block.getTimestamp
  175. && tx.isValid(newBlockDb) == Transaction.VALIDATE_OKE
  176. && totalBytes + tx.getDataLength <= Block.MAX_TRANSACTION_BYTES) {
  177.  
  178. block.addTransaction(tx)
  179. tx.process(newBlockDb)
  180. totalBytes + tx.getDataLength
  181. } else totalBytes
  182. }
  183. }
  184.  
  185. def getNextBlockGeneratingBalance(db: DBSet, block: Block) = {
  186. if (block.getHeight(db) % RETARGET == 0) {
  187. //GET FIRST BLOCK OF TARGET
  188. val firstBlock = (1 to RETARGET - 1).foldLeft(block) { case (bl, _) => bl.getParent(db)}
  189.  
  190. //CALCULATE THE GENERATING TIME FOR LAST 10 BLOCKS
  191. val generatingTime = block.getTimestamp - firstBlock.getTimestamp
  192.  
  193. //CALCULATE EXPECTED FORGING TIME
  194. val expectedGeneratingTime = getBlockTime(block.getGeneratingBalance) * RETARGET * 1000
  195.  
  196. //CALCULATE MULTIPLIER
  197. val multiplier = expectedGeneratingTime / generatingTime.toDouble
  198.  
  199. //CALCULATE NEW GENERATING BALANCE
  200. val generatingBalance = (block.getGeneratingBalance * multiplier).toLong
  201. minMaxBalance(generatingBalance)
  202. } else block.getGeneratingBalance
  203. }
  204.  
  205. def getBaseTarget(generatingBalance: Long) = minMaxBalance(generatingBalance) * getBlockTime(generatingBalance)
  206.  
  207. def getBlockTime(generatingBalance: Long) = {
  208. val percentageOfTotal = minMaxBalance(generatingBalance) / MAX_BALANCE.toDouble
  209. (MIN_BLOCK_TIME + ((MAX_BLOCK_TIME - MIN_BLOCK_TIME) * (1 - percentageOfTotal))).toLong
  210. }
  211.  
  212. def minMaxBalance(generatingBalance: Long) =
  213. if (generatingBalance < MIN_BALANCE) MIN_BALANCE
  214. else if (generatingBalance > MAX_BALANCE) MAX_BALANCE
  215. else generatingBalance
  216. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement