Пример #1
0
        /*
         *
         * Next Private Key = hash( current public key + seed + currentIndex )
         */
        private Wallet next(byte[] seed)
        {
            byte[] currentIndexByte = BitConverter.GetBytes(currentIndex);

            byte[] beforehash = new byte[keyPair.PublicKey.Length + seed.Length + currentIndexByte.Length];

            System.Buffer.BlockCopy(keyPair.PublicKey, 0, beforehash, 0, keyPair.PublicKey.Length);
            System.Buffer.BlockCopy(seed, 0, beforehash, keyPair.PublicKey.Length, seed.Length);
            System.Buffer.BlockCopy(currentIndexByte, 0, beforehash, keyPair.PublicKey.Length + seed.Length, currentIndexByte.Length);

            byte[] nextPrivKey = Hash.ComputeDoubleSHA256(beforehash);

            byte[] nextPubKeyByte;

            EccService.GenerateKeyFromPrivateKey(nextPrivKey, out nextPubKeyByte);

            KeyPair nextKeyPair = new KeyPair
            {
                PrivateKey = nextPrivKey,
                PublicKey  = nextPubKeyByte,
                Address    = BlockchainUtil.ToAddress(nextPubKeyByte),
            };

            return(new Wallet(nextKeyPair, currentIndex + 1));
        }
Пример #2
0
        public Block TryLoadBlock(ByteString id, byte[] data)
        {
            // Data should not exceed the maximum size.
            if (data.Length > MaximumBlockSize)
            {
                throw new ArgumentException(nameof(data));
            }

            // Integrity check.
            var computedId = BlockchainUtil.ComputeBlockId(data);

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

            // Try to deserialize the data for format validity check.
            var block = BlockchainUtil.DeserializeBlock(data);

            lock (Blocks)
            {
                if (Blocks.ContainsKey(id))
                {
                    return(null);
                }
                Blocks.Add(id, data);
            }

            // Schedule the block for execution.
            Executor.ProcessBlock(block);

            return(block);
        }
Пример #3
0
        public IEnumerable <(ByteString, byte[])> LoadAll()
        {
            foreach (var file in Directory.GetFiles(storagePath))
            {
                var bytes = File.ReadAllBytes(file);
                var block = BlockchainUtil.DeserializeBlock(bytes);

                yield return(block.Id, bytes);
            }
        }
Пример #4
0
 static String Encode(byte[] publickKey, int shiftNum){
     //should use hash
     var seed = System.Text.Encoding.ASCII.GetString(publickKey);
     string seedWithShiftnum = seed + shiftNum;
     var it = System.Text.Encoding.ASCII.GetBytes(seedWithShiftnum);
     var it2 = BlockchainUtil.ToAddress(it);
     var it3 = System.Text.Encoding.ASCII.GetString(it2);
     var it4 = Base64Encode(it3);
     Console.WriteLine(it4);
     return it4;
 }
Пример #5
0
        //初期化
        bool LoadConfiguration(string[] args)
        {
            if (args.Length == 0)
            {
                Console.WriteLine("Should provide configuration file path.");
                return(false);
            }

            // config読み込み
            try
            {
                config = JsonConvert.DeserializeObject <Configuration>(
                    File.ReadAllText(Path.GetFullPath(args[0])));
            }
            catch (Exception exp)
            {
                logger.LogError(
                    "Failed to load configuration file. Run 'config' command.",
                    exp);
                return(false);
            }

            // config内の鍵情報を読み込み
            try
            {
                myKeys = KeyPair.LoadFrom(config.KeyPairPath);
            }
            catch (Exception exp)
            {
                logger.LogError(
                    $"Failed to load key from {config.KeyPairPath}.",
                    exp);
                return(false);
            }

            // genesis.binの読み込み
            try
            {
                var bytes = File.ReadAllBytes(config.GenesisPath);
                genesis = BlockchainUtil.DeserializeBlock(bytes);
            }
            catch (Exception exp)
            {
                logger.LogError(
                    $"Failed to load the genesis from {config.GenesisPath}.",
                    exp);
                return(false);
            }

            return(true);
        }
Пример #6
0
        bool LoadConfiguration(string[] args)
        {
            if (args.Length == 0)
            {
                Console.WriteLine("Should provide configuration file path.");
                return(false);
            }

            try
            {
                config = JsonConvert.DeserializeObject <Configuration>(
                    File.ReadAllText(Path.GetFullPath(args[0])));
            }
            catch (Exception exp)
            {
                logger.LogError(
                    "Failed to load configuration file. Run 'config' command.",
                    exp);
                return(false);
            }

            try
            {
                KeyPair seedKeyPair = KeyPair.LoadFrom(config.KeyPairPath);
                hwallet = new Wallet.HierachicalWallet(seedKeyPair);
                hwallet.init(10);
                hwallet.dumpAll();
                System.Threading.Thread.Sleep(3000);
            }
            catch (Exception exp)
            {
                logger.LogError(
                    $"Failed to load key from {config.KeyPairPath}.",
                    exp);
                return(false);
            }
            try
            {
                var bytes = File.ReadAllBytes(config.GenesisPath);
                genesis = BlockchainUtil.DeserializeBlock(bytes);
            }
            catch (Exception exp)
            {
                logger.LogError(
                    $"Failed to load the genesis from {config.GenesisPath}.",
                    exp);
                return(false);
            }

            return(true);
        }
Пример #7
0
        public static void Exec(string[] args)
        {
            byte[] publicKey;
            byte[] privateKey;
            EccService.GenerateKey(out privateKey, out publicKey);

            var json = JsonConvert.SerializeObject(
                new KeyPair
            {
                PrivateKey = privateKey,
                PublicKey  = publicKey,
                Address    = BlockchainUtil.ToAddress(publicKey),
            },
                Formatting.Indented);

            Console.WriteLine(json);
        }
Пример #8
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);
        }
Пример #9
0
        void CheckFloatingBlocks(ByteString waitingBlockId)
        {
            List <ByteString> pendingBlocks;

            if (floatingBlocks.TryGetValue(waitingBlockId, out pendingBlocks))
            {
                foreach (var floatBlockId in pendingBlocks)
                {
                    byte[] data = null;
                    try
                    {
                        lock (InventoryManager.Blocks)
                            data = InventoryManager.Blocks[floatBlockId];
                    }
                    catch { }

                    if (data.IsNull())
                    {
                        continue;
                    }
                    ProcessBlockLocked(BlockchainUtil.DeserializeBlock(data));
                }
            }
        }
Пример #10
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;
        }
Пример #11
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));
        }
Пример #12
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);
        }
Пример #13
0
        bool LoadConfiguration(string[] args)
        {
            if (args.Length == 0)
            {
                Console.WriteLine("Should provide configuration file path.");
                return false;
            }

            try
            {
                config = JsonConvert.DeserializeObject<Configuration>(
                    File.ReadAllText(Path.GetFullPath(args[0])));
            }
            catch (Exception exp)
            {
                logger.LogError(
                    "Failed to load configuration file. Run 'config' command.",
                    exp);
                return false;
            }

            try
            {
                myKeys = KeyPair.LoadFrom(config.KeyPairPath);
            }
            catch (Exception exp)
            {
                logger.LogError(
                    $"Failed to load key from {config.KeyPairPath}.",
                    exp);
                return false;
            }

            try
            {
                var bytes = File.ReadAllBytes(config.GenesisPath);
                genesis = BlockchainUtil.DeserializeBlock(bytes);
            }
            catch (Exception exp)
            {
                logger.LogError(
                    $"Failed to load the genesis from {config.GenesisPath}.",
                    exp);
                return false;
            }

            try
            {
                if (!string.IsNullOrEmpty(config.StoragePath))
                    storage = new Storage(config.StoragePath);
            }
            catch (Exception exp)
            {
                logger.LogError(
                    $@"Failed to set up blockchain storage at {
                        config.StoragePath}.",
                    exp);
                return false;
            }

            return true;
        }
Пример #14
0
 public static String SeedToAddress(byte[] publicKey, Func<String, String> encodeFn){
     var pk = System.Text.Encoding.ASCII.GetString(publicKey);
     var enk = System.Text.Encoding.ASCII.GetBytes( encodeFn(pk) );
     var addr = System.Text.Encoding.ASCII.GetString( BlockchainUtil.ToAddress(enk) );
     return addr;// hash
 }
Пример #15
0
        public void Run(Transaction tx,
                        DateTime blockTime, ulong coinbase = 0,
                        List <TransactionOutput> spentTxo  = null)
        {
            logger.LogDebug($@"Attempt to run TX:{
                tx.Id.ToString().Substring(0, 7)}");

            // Transaction header validity check.
            if (tx.Timestamp > blockTime ||
                !(coinbase == 0 ^ tx.InEntries.Count == 0))
            {
                throw new ArgumentException();
            }

            // In-Entry validity check.
            ulong inSum    = coinbase;
            var   redeemed = new List <TransactionOutput>();
            var   signHash = BlockchainUtil.GetTransactionSignHash(tx.Original);

            foreach (var inEntry in tx.InEntries)
            {
                // Signature check.
                var verified = EccService.Verify(
                    signHash, inEntry.Signature, inEntry.PublicKey);

                // UTXO check. The transaction output must not be spent by
                // previous transactions.
                var txo = new TransactionOutput
                {
                    TransactionId = inEntry.TransactionId,
                    OutIndex      = inEntry.OutEntryIndex,
                };
                var unspent =
                    !(spentTxo?.Contains(txo) ?? false) &&
                    Utxos.TryGetValue(txo, out txo);

                // Recipient address check.
                var addr       = BlockchainUtil.ToAddress(inEntry.PublicKey);
                var redeemable = txo.Recipient.Equals(
                    ByteString.CopyFrom(addr));

                // Sum all the reedemable.
                inSum = checked (inSum + txo.Amount);

                if (!verified || !unspent || !redeemable)
                {
                    throw new ArgumentException();
                }
                redeemed.Add(txo);
            }

            // Out-entry validity check.
            ulong  outSum    = 0;
            ushort outIndex  = 0;
            var    generated = new List <TransactionOutput>();

            foreach (var outEntry in tx.OutEntries)
            {
                if (outEntry.RecipientHash.IsNull() || outEntry.Amount <= 0)
                {
                    throw new ArgumentException();
                }

                // Sum all the transferred.
                outSum = checked (outSum + outEntry.Amount);

                // Create new UTXO entry.
                generated.Add(new TransactionOutput
                {
                    TransactionId = tx.Id,
                    OutIndex      = outIndex++,
                    Recipient     = outEntry.RecipientHash,
                    Amount        = outEntry.Amount,
                });
            }

            // Output exceeds input or coinbase.
            if (outSum > inSum)
            {
                throw new ArgumentException();
            }

            tx.ExecInfo = new TransactionExecInformation
            {
                Coinbase         = coinbase != 0,
                RedeemedOutputs  = redeemed,
                GeneratedOutputs = generated,
                TransactionFee   = inSum - outSum,
            };
        }
Пример #16
0
        void ProcessBlockLocked(Block block)
        {
            var prevId = block.PreviousHash;

            Block prev;

            if (!Blocks.TryGetValue(prevId, out prev))
            {
                // If the previous block is not under the block tree, mark the
                // block as floating.
                List <ByteString> blocks;
                if (!floatingBlocks.TryGetValue(prevId, out blocks))
                {
                    floatingBlocks[prevId] = blocks = new List <ByteString>();
                }
                blocks.Add(block.Id);
                return;
            }

            // Mark the block as the connected block.
            block.Height          = prev.Height + 1;
            block.TotalDifficulty = prev.TotalDifficulty + block.Difficulty;

            // Avoid processing duplicate block from the past.
            if (Blocks.ContainsKey(block.Id))
            {
                return;
            }
            Blocks.Add(block.Id, block);

            // If the block difficulty does not surpass the current latest,
            // skip the execution.  Once the descendant block comes later,
            // evaluate the difficulty then again.
            if (Latest.TotalDifficulty >= block.TotalDifficulty)
            {
                CheckFloatingBlocks(block.Id);
                return;
            }

            // Otherwise, try to execute the block.  Considering the block folk,
            // first revert all the applied blocks prior to the fork point in
            // past blocks, if exists.  Then apply blocks after the fork.
            var fork = BlockchainUtil.LowestCommonAncestor(
                Latest, block, Blocks);
            var revertingChain = Latest.Ancestors(Blocks)
                                 .TakeWhile(x => !x.Id.Equals(fork.Id))
                                 .ToList();
            var applyingChain = block.Ancestors(Blocks)
                                .TakeWhile(x => !x.Id.Equals(fork.Id))
                                .Reverse()
                                .ToList();

            revertingChain.ForEach(Revert);

            int?failureIndex = null;

            for (var i = 0; i < applyingChain.Count; i++)
            {
                var applyBlock = applyingChain[i];
                try { Run(applyBlock); }
                catch
                {
                    // The block was invalid.  Revert.
                    PurgeBlock(applyBlock.Id);

                    failureIndex = i;
                    break;
                }

                Apply(applyBlock);
            }

            if (failureIndex.HasValue)
            {
                // Failure occurred during the block execution.  Perform
                // opposite to revert to the state before the execution.
                applyingChain.Take(failureIndex.Value)
                .Reverse().ToList().ForEach(Revert);

                revertingChain.Reverse();
                revertingChain.ForEach(Apply);
                return;
            }

            CheckFloatingBlocks(block.Id);
        }
Пример #17
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);
        }//ネットワーク全体にもらったブロックを発信する