public BlockMsg CreateNewBlock(string minerName, string accountId = null) { var accountDac = new AccountDac(); var blockDac = new BlockDac(); var outputDac = new OutputDac(); var txDac = new TransactionDac(); var txPool = TransactionPool.Instance; var transactionMsgs = new List <TransactionMsg>(); long lastBlockHeight = -1; string lastBlockHash = Base16.Encode(HashHelper.EmptyHash()); long lastBlockBits = -1; var blockEntity = blockDac.SelectLast(); if (blockEntity != null) { lastBlockHeight = blockEntity.Height; lastBlockHash = blockEntity.Hash; lastBlockBits = blockEntity.Bits; } long totalSize = 0; long maxSize = BlockSetting.MAX_BLOCK_SIZE - (500 * 1024); var poolItemList = txPool.MainPool.OrderByDescending(t => t.FeeRate).ToList(); var index = 0; while (totalSize < maxSize && index < poolItemList.Count) { var tx = poolItemList[index].Transaction; if (tx != null && transactionMsgs.Where(t => t.Hash == tx.Hash).Count() == 0) { if (txDac.SelectByHash(tx.Hash) == null) { transactionMsgs.Add(tx); totalSize += tx.Size; } else { txPool.RemoveTransaction(tx.Hash); } } else { break; } index++; } var minerAccount = accountDac.SelectDefaultAccount(); if (accountId != null) { var account = accountDac.SelectById(accountId); if (account != null && !string.IsNullOrWhiteSpace(account.PrivateKey)) { minerAccount = account; } } BlockMsg newBlockMsg = new BlockMsg(); BlockHeaderMsg headerMsg = new BlockHeaderMsg(); newBlockMsg.Header = headerMsg; headerMsg.Height = lastBlockHeight + 1; headerMsg.PreviousBlockHash = lastBlockHash; long totalAmount = 0; long totalFee = 0; foreach (var tx in transactionMsgs) { long totalInputsAmount = 0L; long totalOutputAmount = 0L; foreach (var input in tx.Inputs) { var utxo = outputDac.SelectByHashAndIndex(input.OutputTransactionHash, input.OutputIndex); if (utxo != null) { totalInputsAmount += utxo.Amount; } } foreach (var output in tx.Outputs) { totalOutputAmount += output.Amount; } totalAmount += totalOutputAmount; totalFee += (totalInputsAmount - totalOutputAmount); } var work = new POW(headerMsg.Height); BlockMsg previous4032Block = null; if (headerMsg.Height > POW.DiffiucltyAdjustStep) { previous4032Block = this.GetBlockMsgByHeight(headerMsg.Height - POW.DiffiucltyAdjustStep); } var newBlockReward = work.GetNewBlockReward(); headerMsg.Bits = work.CalculateNextWorkTarget(lastBlockHeight, lastBlockBits, previous4032Block); headerMsg.TotalTransaction = transactionMsgs.Count + 1; var coinbaseTxMsg = new TransactionMsg(); coinbaseTxMsg.Timestamp = Time.EpochTime; coinbaseTxMsg.Locktime = 0; var coinbaseInputMsg = new InputMsg(); coinbaseTxMsg.Inputs.Add(coinbaseInputMsg); coinbaseInputMsg.OutputIndex = 0; coinbaseInputMsg.OutputTransactionHash = Base16.Encode(HashHelper.EmptyHash()); coinbaseInputMsg.UnlockScript = Script.BuildMinerScript(minerName); coinbaseInputMsg.Size = coinbaseInputMsg.UnlockScript.Length; var coinbaseOutputMsg = new OutputMsg(); coinbaseTxMsg.Outputs.Add(coinbaseOutputMsg); coinbaseOutputMsg.Amount = newBlockReward + totalFee; coinbaseOutputMsg.LockScript = Script.BuildLockScipt(minerAccount.Id); coinbaseOutputMsg.Size = coinbaseOutputMsg.LockScript.Length; coinbaseOutputMsg.Index = 0; coinbaseTxMsg.Hash = coinbaseTxMsg.GetHash(); newBlockMsg.Transactions.Insert(0, coinbaseTxMsg); foreach (var tx in transactionMsgs) { newBlockMsg.Transactions.Add(tx); } return(newBlockMsg); }
public bool VerifyBlock(BlockMsg newBlock) { if (this.exists(newBlock.Header.Hash)) { throw new CommonException(ErrorCode.Engine.Block.Verify.BLOCK_HAS_BEEN_EXISTED); } if (newBlock.Header.Hash != newBlock.Header.GetHash()) { throw new CommonException(ErrorCode.Engine.Block.Verify.BLOCK_HASH_ERROR); } var blockComponent = new BlockComponent(); var previousBlock = this.GetBlockMsgByHash(newBlock.Header.PreviousBlockHash); if (newBlock.Header.Height > 0 && previousBlock == null) { throw new CommonException(ErrorCode.Engine.Block.Verify.PREV_BLOCK_NOT_EXISTED); } if ((newBlock.Header.Timestamp > Time.EpochTime && (newBlock.Header.Timestamp - Time.EpochTime) > 2 * 60 * 60 * 1000) || (previousBlock != null && newBlock.Header.Timestamp <= previousBlock.Header.Timestamp)) { throw new CommonException(ErrorCode.Engine.Block.Verify.BLOCK_TIME_IS_ERROR); } if (newBlock.Serialize().Length > BlockSetting.MAX_BLOCK_SIZE) { throw new CommonException(ErrorCode.Engine.Block.Verify.BLOCK_SIZE_LARGE_THAN_LIMIT); } var work = new POW(newBlock.Header.Height); var txComponent = new TransactionComponent(); if (newBlock.Transactions.Count > 0) { var coinbase = newBlock.Transactions[0]; var reward = work.GetNewBlockReward(); var totalFee = 0L; if (newBlock.Transactions.Count > 1) { for (int i = 1; i < newBlock.Transactions.Count; i++) { long fee; if (txComponent.VerifyTransaction(newBlock.Transactions[i], out fee)) { totalFee += fee; } else { return(false); } } } if (coinbase.Inputs.Count != 1 || coinbase.Outputs.Count != 1 || coinbase.Inputs[0].OutputTransactionHash != Base16.Encode(HashHelper.EmptyHash())) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.COINBASE_FORMAT_ERROR); } else { var output = coinbase.Outputs[0]; if (output.Amount > BlockSetting.OUTPUT_AMOUNT_MAX) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.OUTPUT_EXCEEDED_THE_LIMIT); } if (!Script.VerifyLockScriptFormat(output.LockScript)) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.SCRIPT_FORMAT_ERROR); } if (output.Amount != (totalFee + reward)) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.COINBASE_OUTPUT_AMOUNT_ERROR); } } } var prev4032Block = this.GetBlockMsgByHeight(newBlock.Header.Height - POW.DiffiucltyAdjustStep); long previousBlockHeight = -1; long previousBlockBits = -1; if (previousBlock != null) { previousBlockHeight = previousBlock.Header.Height; previousBlockBits = previousBlock.Header.Bits; } if (work.CalculateNextWorkTarget(previousBlockHeight, previousBlockBits, prev4032Block) != newBlock.Header.Bits) { throw new CommonException(ErrorCode.Engine.Block.Verify.BITS_IS_WRONG); } var hashResult = this.GetMiningWorkResult(newBlock); if (work.Verify(newBlock.Header.Bits, hashResult)) { return(true); } else { throw new CommonException(ErrorCode.Engine.Block.Verify.POW_VERIFY_FAIL); } }