コード例 #1
0
        public bool VerifyTransaction(TransactionMsg transaction, out long txFee)
        {
            var blockComponent = new BlockComponent();

            //step 0
            if (transaction.Hash != transaction.GetHash())
            {
                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);
            }

            long totalOutput = 0;
            long 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;
            }

            foreach (var input in transaction.Inputs)
            {
                if (!Script.VerifyUnlockScriptFormat(input.UnlockScript))
                {
                    throw new CommonException(ErrorCode.Engine.Transaction.Verify.SCRIPT_FORMAT_ERROR);
                }

                long   amount;
                string lockScript;
                long   blockHeight;
                var    result = this.getOutput(input.OutputTransactionHash, input.OutputIndex, out amount, out lockScript, out blockHeight);

                if (result)
                {
                    var utxoTx = this.GetTransactionMsgByHash(input.OutputTransactionHash);

                    if (utxoTx.InputCount == 1 && utxoTx.Inputs[0].OutputTransactionHash == Base16.Encode(HashHelper.EmptyHash()) &&
                        (blockHeight < 0 || (blockComponent.GetLatestHeight() - blockHeight < 100)))
                    {
                        throw new CommonException(ErrorCode.Engine.Transaction.Verify.COINBASE_NEED_100_CONFIRMS);
                    }

                    if (Time.EpochTime < utxoTx.Locktime)
                    {
                        throw new CommonException(ErrorCode.Engine.Transaction.Verify.TRANSACTION_IS_LOCKED);
                    }

                    //check whether contain two or more same utxo in one transaction
                    var count = transaction.Inputs.Where(i => i.OutputTransactionHash == input.OutputTransactionHash && i.OutputIndex == input.OutputIndex).Count();

                    if (count > 1 || this.checkOutputSpent(transaction.Hash, input.OutputTransactionHash, input.OutputIndex))
                    {
                        throw new CommonException(ErrorCode.Engine.Transaction.Verify.UTXO_HAS_BEEN_SPENT);
                    }

                    if (!Script.VerifyLockScriptByUnlockScript(input.OutputTransactionHash, input.OutputIndex, lockScript, input.UnlockScript))
                    {
                        throw new CommonException(ErrorCode.Engine.Transaction.Verify.UTXO_UNLOCK_FAIL);
                    }

                    totalInput += 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);
        }
コード例 #2
0
        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 txComponent = new TransactionComponent();

            var newBlockReward = POC.GetNewBlockReward(newBlock.Header.Height);
            var prevStepBlock  = GetBlockMsgByHeight(newBlock.Header.Height - POC.DIFFIUCLTY_ADJUST_STEP - 1);
            var minerInfo      = string.Empty;

            if (newBlock.Transactions.Count > 0)
            {
                var coinbase = newBlock.Transactions[0];
                var totalFee = 0L;

                minerInfo = Encoding.UTF8.GetString(Base16.Decode(coinbase.Inputs[0].UnlockScript)).Split("`")[0];

                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 + newBlockReward))
                    {
                        throw new CommonException(ErrorCode.Engine.Transaction.Verify.COINBASE_OUTPUT_AMOUNT_ERROR);
                    }
                }
            }

            var pool = new MiningPoolComponent().GetMiningPoolByName(minerInfo);

            if (pool == null)
            {
                throw new CommonException(ErrorCode.Engine.Block.Verify.MINING_POOL_NOT_EXISTED);
            }

            if (!POC.VerifyBlockSignature(newBlock.Header.PayloadHash, newBlock.Header.BlockSignature, pool.PublicKey))
            {
                throw new CommonException(ErrorCode.Engine.Block.Verify.BLOCK_SIGNATURE_IS_ERROR);
            }

            long previousBlockHeight = -1;
            long previousBlockBits   = -1;

            if (previousBlock != null)
            {
                previousBlockHeight = previousBlock.Header.Height;
                previousBlockBits   = previousBlock.Header.Bits;
            }

            if (POC.CalculateBaseTarget(newBlock.Header.Height, previousBlock, prevStepBlock) != newBlock.Header.Bits)
            {
                throw new CommonException(ErrorCode.Engine.Block.Verify.BITS_IS_WRONG);
            }

            var targetResult = POC.CalculateTargetResult(newBlock);

            if (POC.Verify(newBlock.Header.Bits, targetResult))
            {
                return(true);
            }
            else
            {
                throw new CommonException(ErrorCode.Engine.Block.Verify.POC_VERIFY_FAIL);
            }
        }
コード例 #3
0
ファイル: BlockComponent.cs プロジェクト: BitNumbers/FiiiCoin
        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);
            }
        }