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 GetTxOutSetInfoOM GetTxOutSetInfo() { GetTxOutSetInfoOM result = new GetTxOutSetInfoOM(); var blockComponent = new BlockComponent(); result.height = GlobalParameters.LocalHeight; result.bestblock = BlockDac.Default.GetBlockHashByHeight(result.height); var accounts = AccountDac.Default.GetMyAccountBook(); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); var confirmedUtxos = UtxoSetDac.Default.GetMyUnspentUtxoKeys().ToArray(); stopwatch.Stop(); LogHelper.Warn($"[accounts Count] :: {accounts.Count}"); LogHelper.Warn($"[GetMyUnspentUtxoKeys] use time :: {stopwatch.ElapsedMilliseconds}"); var transacionCount = confirmedUtxos.Select(x => x.Split("_")[0]).Distinct().Count(); result.transactions = transacionCount; result.txouts = confirmedUtxos.Count(); result.total_amount = UtxoSetDac.Default.GetConfirmedAmount() - TransactionPoolDac.Default.UseBalanceInPool; return(result); }
public bool VerifyTransaction(TransactionMsg transaction, out long txFee, out long totalOutput, out long totalInput) { var blockComponent = new BlockComponent(); txFee = 0; //compatible with old node if (transaction.Locktime > 0 && transaction.ExpiredTime == transaction.Locktime) { transaction.ExpiredTime = 0; } //step 0 if (transaction.Hash != transaction.GetHash()) { LogHelper.Error("Tx Hash Error:" + transaction.Hash); LogHelper.Error("Timestamp:" + transaction.Timestamp); LogHelper.Error("Locktime:" + transaction.Locktime); LogHelper.Error("ExpiredTime:" + transaction.ExpiredTime); LogHelper.Error("InputCount:" + transaction.InputCount); LogHelper.Error("OutputCount:" + transaction.OutputCount); throw new CommonException(ErrorCode.Engine.Transaction.Verify.TRANSACTION_HASH_ERROR); } //step 1 if (transaction.InputCount == 0 || transaction.OutputCount == 0) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.INPUT_AND_OUTPUT_CANNOT_BE_EMPTY); } //step 2 if (transaction.Hash == Base16.Encode(HashHelper.EmptyHash())) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.HASH_CANNOT_BE_EMPTY); } //step 3 if (transaction.Locktime < 0 || transaction.Locktime > (Time.EpochTime + BlockSetting.LOCK_TIME_MAX)) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.LOCK_TIME_EXCEEDED_THE_LIMIT); } //step 4 if (transaction.Serialize().Length < BlockSetting.TRANSACTION_MIN_SIZE) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.TRANSACTION_SIZE_BELOW_THE_LIMIT); } //step 5 if (this.existsInDB(transaction.Hash)) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.TRANSACTION_HAS_BEEN_EXISTED); } totalOutput = 0; totalInput = 0; foreach (var output in transaction.Outputs) { if (output.Amount <= 0 || 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); } totalOutput += output.Amount; } var count = transaction.Inputs.Distinct().Count(); if (count != transaction.Inputs.Count) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.UTXO_DUPLICATED_IN_ONE_TRANSACTION); } foreach (var input in transaction.Inputs) { if (!Script.VerifyUnlockScriptFormat(input.UnlockScript)) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.SCRIPT_FORMAT_ERROR); } var utxo = UtxoSetDac.Default.Get(input.OutputTransactionHash, input.OutputIndex); if (utxo != null) { if (!utxo.IsConfirmed(GlobalParameters.LocalHeight)) { if (utxo.IsCoinbase) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.COINBASE_NEED_100_CONFIRMS); } else { return(false); } } if (Time.EpochTime < utxo.Locktime) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.TRANSACTION_IS_LOCKED); } if (utxo.IsSpent()) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.UTXO_HAS_BEEN_SPENT); } if (!Script.VerifyLockScriptByUnlockScript(input.OutputTransactionHash, input.OutputIndex, utxo.LockScript, input.UnlockScript)) { throw new CommonException(ErrorCode.Engine.Transaction.Verify.UTXO_UNLOCK_FAIL); } totalInput += utxo.Amount; } else { //not found output, wait for other transactions or blocks; txFee = 0; return(false); } } 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); } txFee = totalInput - totalOutput; return(true); }