public bool VerifyBlock(BlockMsg newBlock) { //校验区块的基本信息 var result = VerifyBlockBasic(newBlock); if (!result) { return(false); } var txComponent = new TransactionComponent(); var blockComponent = new BlockComponent(); //校验交易信息 var totalFee = 0L; VerifyTransactionModel model = new VerifyTransactionModel(); model.block = newBlock; model.localHeight = blockComponent.GetLatestHeight(); foreach (var item in newBlock.Transactions) { long fee; model.transaction = item; if (txComponent.VerifyTransactionMsg(model, out fee)) { totalFee += fee; } else { return(false); } } if (Heights.Contains(newBlock.Header.Height)) { return(true); } var newBlockReward = POC.GetNewBlockReward(newBlock.Header.Height); var coinbaseAmount = newBlock.Transactions[0].Outputs[0].Amount; if (coinbaseAmount < 0 || coinbaseAmount != (totalFee + newBlockReward)) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.COINBASE_OUTPUT_AMOUNT_ERROR); } return(true); }
public bool VerifyTransactionMsg(VerifyTransactionModel model, out long fee) { var transaction = model.transaction; //校验锁定时间 if (transaction.Locktime > 0 && transaction.ExpiredTime == transaction.Locktime) { transaction.ExpiredTime = 0; } //校验HASH值 if (transaction.Hash != transaction.GetHash()) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.TRANSACTION_HASH_ERROR); } //交易必须包含输入和输出 if (transaction.InputCount == 0 || transaction.OutputCount == 0) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.INPUT_AND_OUTPUT_CANNOT_BE_EMPTY); } //校验交易的时间 if (transaction.Locktime < 0 || transaction.Locktime > (Time.EpochTime + BlockSetting.LOCK_TIME_MAX)) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.LOCK_TIME_EXCEEDED_THE_LIMIT); } //校验交易量 if (transaction.Serialize().Length < BlockSetting.TRANSACTION_MIN_SIZE) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.TRANSACTION_SIZE_BELOW_THE_LIMIT); } return(VerifyTransactionData(model, out fee)); }
public bool VerifyTransactionData(VerifyTransactionModel model, out long fee) { long totalOutput = 0; long totalInput = 0; var transaction = model.transaction; var block = model.block; var localHeight = model.localHeight; List <Output> outputEntities = new List <Output>(); List <Input> inputEntities = new List <Input>(); #region 校验和转换 接收地址 foreach (var output in transaction.Outputs) { if ((output.Amount <= 0 || output.Amount > BlockSetting.OUTPUT_AMOUNT_MAX) && !Heights.Contains(block.Header.Height)) { 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); } var outputEntity = output.ConvertToEntiry(transaction, block); outputEntities.Add(outputEntity); totalOutput += output.Amount; } #endregion #region 判断是不是在一个区块内重复花费(24696之前有错误数据) if (block.Header.Height <= 24696) { var inputsCount = transaction.Inputs.Select(x => x.OutputTransactionHash + x.OutputIndex).Distinct().Count(); if (inputsCount < transaction.Inputs.Count) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.UTXO_NOT_EXISTED); } } #endregion var firstInput = transaction.Inputs[0]; bool isCoinbase = firstInput.OutputTransactionHash == COINBASE_INPUT_HASH; if (isCoinbase) { #region 如果是Coinbase var inputps = new InputExtensionParams(); inputps.BlockHash = block.Header.Hash; inputps.InputAccountId = null; inputps.InputAmount = 0; inputps.TransactionHash = transaction.Hash; var inputEntity = firstInput.ConvertToEntiry(inputps); totalInput += 0; inputEntities.Add(inputEntity); isCoinbase = true; #endregion } else { foreach (var input in transaction.Inputs) { if (!Script.VerifyUnlockScriptFormat(input.UnlockScript)) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.SCRIPT_FORMAT_ERROR); } var output = UtxoSetDac.Default.Get(input.OutputTransactionHash, input.OutputIndex); if (output == null) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.UTXO_NOT_EXISTED); } //是否已经上链了(存在重复写入的情况) if (output.IsSpent() && output.SpentHeight != block.Header.Height && block.Header.Height > 24696) { LogHelper.Warn($"transaction.Hash:{transaction.Hash}"); throw new CommonException(ErrorCode.Engine.Transaction.Verify.UTXO_HAS_BEEN_SPENT); } long blockHeight = output.BlockHeight; //判断挖矿的区块等待100个确认 if (output.IsCoinbase && !output.IsConfirmed(block.Header.Height)) { if (output.IsCoinbase) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.COINBASE_NEED_100_CONFIRMS); } else { throw new CommonException(ErrorCode.Engine.Transaction.Verify.UTXO_NEED_6_CONFIRMS); } } //判断余额是否已经解锁 if (Time.EpochTime < output.Locktime) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.TRANSACTION_IS_LOCKED); } //校验存币时间,当前时间大于存币过期时间才能使用 if (output.DepositTime > 0 && output.DepositTime >= Time.EpochTime) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.DEPOSIT_TIME_NOT_EXPIRED); } string lockScript = output.LockScript; if (!Script.VerifyLockScriptByUnlockScript(input.OutputTransactionHash, input.OutputIndex, lockScript, input.UnlockScript)) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.UTXO_UNLOCK_FAIL); } var inputps = new InputExtensionParams(); inputps.BlockHash = block.Header.Hash; inputps.InputAccountId = output.Account; inputps.InputAmount = output.Amount; inputps.TransactionHash = transaction.Hash; var inputEntity = input.ConvertToEntiry(inputps); totalInput += output.Amount; inputEntities.Add(inputEntity); } if (totalOutput >= totalInput) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.OUTPUT_LARGE_THAN_INPUT); } if ((totalInput - totalOutput) < BlockSetting.TRANSACTION_MIN_FEE) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.TRANSACTION_FEE_IS_TOO_FEW); } } if (totalInput > BlockSetting.INPUT_AMOUNT_MAX) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.INPUT_EXCEEDED_THE_LIMIT); } if (totalOutput > BlockSetting.OUTPUT_AMOUNT_MAX) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.OUTPUT_EXCEEDED_THE_LIMIT); } if (isCoinbase) { fee = 0; } else { fee = totalInput - totalOutput; } return(true); }