示例#1
0
        bool TryAddTransactionToMemoryPool(ByteString id, byte[] data)
        {
            // Data should not exceed the maximum size.
            if (data.Length > MaximumTransactionSize)
            {
                throw new ArgumentException();
            }

            // Integrity check.
            var computedId = Hash.ComputeDoubleSHA256(data);

            if (!ByteString.CopyFrom(computedId).Equals(id))
            {
                throw new ArgumentException();
            }

            if (MemoryPool.ContainsKey(id))
            {
                return(false);
            }

            var tx = BlockchainUtil.DeserializeTransaction(data);

            // Ignore the coinbase transactions.
            if (tx.InEntries.Count == 0)
            {
                return(false);
            }

            lock (MemoryPool)
            {
                if (MemoryPool.ContainsKey(id))
                {
                    return(false);
                }
                MemoryPool.Add(id, tx);
            }

            return(true);
        }
示例#2
0
        void Run(Block block)
        {
            logger.LogDebug($@"Attempt to run Block:{
                block.Id.ToString().Substring(0, 7)}");

            Debug.Assert(Latest.Id.Equals(block.PreviousHash));
            if (!block.ParsedTransactions.IsNull())
            {
                return;
            }

            var blockTime  = block.Timestamp;
            var txCount    = block.Transactions.Count;
            var rootTxHash = BlockchainUtil.RootHashTransactionIds(
                block.TransactionIds);
            var difficulty = BlockParameter.GetNextDifficulty(
                block.Ancestors(Blocks).Skip(1));

            // Block header validity check.

            // The block timestamp must be the past time after previous block.
            // The block must contain at least one transaction: coinbase.
            // The number of transaction IDs and actual data must match.
            // Transaction Root must be the hash of all TX IDs concatenated.
            // The block must have a pointer to the previous block.
            // The block's difficulty must be within the computed range.
            // The Block ID has greater difficulty than the computed difficulty.
            if (blockTime > DateTime.UtcNow ||
                blockTime < Latest.Timestamp ||
                txCount == 0 || txCount != block.TransactionIds.Count ||
                !rootTxHash.SequenceEqual(block.TransactionRootHash) ||
                !Latest.Id.Equals(block.PreviousHash) ||
                block.Difficulty >= difficulty * (1 + 1e-15) ||
                block.Difficulty <= difficulty * (1 - 1e-15) ||
                Hash.Difficulty(block.Id.ToByteArray()) < block.Difficulty)
            {
                throw new ArgumentException();
            }

            // Deserialize transactions and check IDs.
            var transactions = new Transaction[txCount];

            for (var i = 0; i < txCount; i++)
            {
                var tx = BlockchainUtil.DeserializeTransaction(
                    block.Transactions[i]);

                if (!tx.Id.Equals(block.TransactionIds[i]))
                {
                    throw new ArgumentException();
                }
                transactions[i] = tx;
            }

            // Run normal transactions.
            ulong coinbase = BlockParameter.GetCoinbase(Latest.Height + 1);
            var   spent    = new List <TransactionOutput>();

            for (var i = 1; i < txCount; i++)
            {
                Run(transactions[i], blockTime, spentTxo: spent);

                // Collect all transaction fees to pay to miner.  Accumulate
                // spent transaction outputs.
                var exec = transactions[i].ExecInfo;
                coinbase += exec.TransactionFee;
                spent.AddRange(exec.RedeemedOutputs);
            }

            // Run the coinbase transaction.
            Run(transactions[0], blockTime, coinbase);

            block.Height             = Latest.Height + 1;
            block.ParsedTransactions = transactions;
            block.TotalDifficulty    = Latest.TotalDifficulty + block.Difficulty;
        }
示例#3
0
        public Transaction SendTo(
            HashSet <TransactionOutput> utxos,
            ByteString recipient, ulong amount)
        {
            // TODO: You should consider transaction fee.

            // Extract my spendable UTXOs.
            ulong sum       = 0;
            var   inEntries = new List <InEntry>();

            foreach (var utxo in utxos)
            {
                if (!utxo.Recipient.Equals(Address))
                {
                    continue;
                }
                inEntries.Add(new InEntry
                {
                    TransactionId = utxo.TransactionId,
                    OutEntryIndex = utxo.OutIndex,
                });

                sum += utxo.Amount;
                if (sum >= amount)
                {
                    goto CreateOutEntries;
                }
            }

            throw new ArgumentException(
                      "Insufficient fund.", nameof(amount));

CreateOutEntries:
            // Create list of out entries.  It should contain fund transfer and
            // change if necessary.  Also the sum of outputs must be less than
            // that of inputs.  The difference will be collected as transaction
            // fee.
            var outEntries = new List <OutEntry>
            {
                new OutEntry
                {
                    RecipientHash = recipient,
                    Amount        = amount,
                },
            };

            var change = sum - amount;

            if (change != 0)
            {
                outEntries.Add(new OutEntry
                {
                    RecipientHash = Address,
                    Amount        = change,
                });
            }

            // Construct to-be-signed transaction.
            var transaction = new Transaction
            {
                Timestamp  = DateTime.UtcNow,
                InEntries  = inEntries,
                OutEntries = outEntries,
            };

            // Take a transaction signing hash and sign against it.  Since
            // wallet contains a single key pair, single signing is sufficient.
            var signHash = BlockchainUtil.GetTransactionSignHash(
                Serialize(transaction));
            var signature = EccService.Sign(
                signHash, keyPair.PrivateKey, keyPair.PublicKey);

            foreach (var inEntry in inEntries)
            {
                inEntry.PublicKey = keyPair.PublicKey;
                inEntry.Signature = signature;
            }

            var bytes = Serialize(transaction);

            return(BlockchainUtil.DeserializeTransaction(bytes));
        }
示例#4
0
        async Task HandleBody(InventoryMessage message, int peerId)
        {
            // Data should not exceed the maximum size.
            var data = message.Data;

            if (data.Length > MaximumBlockSize)
            {
                throw new ArgumentException();
            }

            // ハッシュ値正しい?
            var id = message.IsBlock ?
                     BlockchainUtil.ComputeBlockId(data) :
                     Hash.ComputeDoubleSHA256(data);

            if (!ByteString.CopyFrom(id).Equals(message.ObjectId))
            {
                return;
            }

            if (message.IsBlock)
            {
                // ミューテックス
                lock (Blocks)
                {
                    if (Blocks.ContainsKey(message.ObjectId))
                    {
                        return;
                    }
                    // ハッシュテーブルに追加
                    Blocks.Add(message.ObjectId, data);
                }

                var block = BlockchainUtil.DeserializeBlock(data);
                // 前のブロックも知らなかったら前のももらう
                var prevId = block.PreviousHash;
                if (!Blocks.ContainsKey(prevId))
                {
                    await ConnectionManager.SendAsync(new InventoryMessage
                    {
                        Type     = Request,
                        IsBlock  = true,
                        ObjectId = prevId,
                    }, peerId);
                }
                Executor.ProcessBlock(block);
            }
            else
            {
                if (MemoryPool.ContainsKey(message.ObjectId))
                {
                    return;
                }

                var tx = BlockchainUtil.DeserializeTransaction(data);

                // Ignore the coinbase transactions.
                if (tx.InEntries.Count == 0)
                {
                    return;
                }

                lock (MemoryPool)
                {
                    if (MemoryPool.ContainsKey(message.ObjectId))
                    {
                        return;
                    }
                    MemoryPool.Add(message.ObjectId, tx);
                }
            }

            message.Type = Advertise;
            message.Data = null;
            // 他の人に教えてあげる
            await ConnectionManager.BroadcastAsync(message, peerId);
        }
示例#5
0
        async Task HandleBody(InventoryMessage message, int peerId)
        {
            // Data should not exceed the maximum size.
            var data = message.Data;

            if (data.Length > MaximumBlockSize)
            {
                throw new ArgumentException();
            }

            var id = message.IsBlock ?
                     BlockchainUtil.ComputeBlockId(data) :
                     Hash.ComputeDoubleSHA256(data);

            if (!ByteString.CopyFrom(id).Equals(message.ObjectId))
            {
                return;
            }

            if (message.IsBlock)
            {
                lock (Blocks)//並列処理を避ける
                {
                    if (Blocks.ContainsKey(message.ObjectId))
                    {
                        return;
                    }
                    Blocks.Add(message.ObjectId, data);
                }

                var prevId = Deserialize <Block>(data).PreviousHash;
                if (!Blocks.ContainsKey(prevId))
                {
                    await ConnectionManager.SendAsync(new InventoryMessage//当該ブロックの前のブロックを持っていない場合はそれもRequestする(前のblockのIDが必要なため
                    {
                        Type     = Request,
                        IsBlock  = true,
                        ObjectId = prevId,
                    }, peerId);
                }
                else
                {
                    Executor.ProcessBlock(data, prevId);
                }
            }
            else
            {
                if (MemoryPool.ContainsKey(message.ObjectId))
                {
                    return;
                }

                var tx = BlockchainUtil.DeserializeTransaction(data);

                // Ignore the coinbase transactions.
                if (tx.InEntries.Count == 0)
                {
                    return;
                }

                lock (MemoryPool)
                {
                    if (MemoryPool.ContainsKey(message.ObjectId))
                    {
                        return;
                    }
                    MemoryPool.Add(message.ObjectId, tx);
                }
            }

            message.Type = Advertise;
            message.Data = null;
            await ConnectionManager.BroadcastAsync(message, peerId);
        }//ネットワーク全体にもらったブロックを発信する