/* * * 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)); }
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); }
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); } }
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; }
//初期化 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); }
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); }
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); }
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); }
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)); } } }
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; }
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)); }
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); }
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; }
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 }
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, }; }
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); }
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); }//ネットワーク全体にもらったブロックを発信する