using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Security.Cryptography; using System.Text; namespace BlockChainDemo { public class BlockChain { private List _currentTransactions = new List(); private List _chain = new List(); private List _nodes = new List(); private Block _lastBlock => _chain.Last(); public string NodeId { get; private set; } //ctor public BlockChain() { NodeId = Guid.NewGuid().ToString().Replace("-", ""); CreateNewBlock(proof: 100, previousHash: "1"); //genesis block } //private functionality private void RegisterNode(string address) { _nodes.Add(new Node { Address = new Uri(address) }); } private bool IsValidChain(List chain) { Block block = null; Block lastBlock = chain.First(); int currentIndex = 1; while (currentIndex < chain.Count) { block = chain.ElementAt(currentIndex); Debug.WriteLine($"{lastBlock}"); Debug.WriteLine($"{block}"); Debug.WriteLine("----------------------------"); //Check that the hash of the block is correct if (block.PreviousHash != GetHash(lastBlock)) return false; //Check that the Proof of Work is correct if (!IsValidProof(lastBlock.Proof, block.Proof, lastBlock.PreviousHash)) return false; lastBlock = block; currentIndex++; } return true; } private bool ResolveConflicts() { List newChain = null; int maxLength = _chain.Count; foreach (Node node in _nodes) { var url = new Uri(node.Address, "/chain"); var request = (HttpWebRequest)WebRequest.Create(url); var response = (HttpWebResponse)request.GetResponse(); if (response.StatusCode == HttpStatusCode.OK) { var model = new { chain = new List(), length = 0 }; string json = new StreamReader(response.GetResponseStream()).ReadToEnd(); var data = JsonConvert.DeserializeAnonymousType(json, model); if (data.chain.Count > _chain.Count && IsValidChain(data.chain)) { maxLength = data.chain.Count; newChain = data.chain; } } } if (newChain != null) { _chain = newChain; return true; } return false; } private Block CreateNewBlock(int proof, string previousHash = null) { var block = new Block { Index = _chain.Count, Timestamp = DateTime.UtcNow, Transactions = _currentTransactions.ToList(), Proof = proof, PreviousHash = previousHash ?? GetHash(_chain.Last()) }; _currentTransactions.Clear(); _chain.Add(block); return block; } private int CreateProofOfWork(int lastProof, string previousHash) { int proof = 0; while (!IsValidProof(lastProof, proof, previousHash)) proof++; return proof; } private bool IsValidProof(int lastProof, int proof, string previousHash) { string guess = $"{lastProof}{proof}{previousHash}"; string result = GetSha256(guess); return result.StartsWith("0000"); } private string GetHash(Block block) { string blockText = JsonConvert.SerializeObject(block); return GetSha256(blockText); } private string GetSha256(string data) { var sha256 = new SHA256Managed(); var hashBuilder = new StringBuilder(); byte[] bytes = Encoding.Unicode.GetBytes(data); byte[] hash = sha256.ComputeHash(bytes); foreach (byte x in hash) hashBuilder.Append($"{x:x2}"); return hashBuilder.ToString(); } //web server calls internal string Mine() { int proof = CreateProofOfWork(_lastBlock.Proof, _lastBlock.PreviousHash); CreateTransaction(sender: "0", recipient: NodeId, amount: 1); Block block = CreateNewBlock(proof /*, _lastBlock.PreviousHash*/); var response = new { Message = "New Block Forged", Index = block.Index, Transactions = block.Transactions.ToArray(), Proof = block.Proof, PreviousHash = block.PreviousHash }; return JsonConvert.SerializeObject(response); } internal string GetFullChain() { var response = new { chain = _chain.ToArray(), length = _chain.Count }; return JsonConvert.SerializeObject(response); } internal string RegisterNodes(string[] nodes) { var builder = new StringBuilder(); foreach (string node in nodes) { string url = $"http://{node}"; RegisterNode(url); builder.Append($"{url}, "); } builder.Insert(0, $"{nodes.Count()} new nodes have been added: "); string result = builder.ToString(); return result.Substring(0, result.Length - 2); } internal string Consensus() { bool replaced = ResolveConflicts(); string message = replaced ? "was replaced" : "is authoritive"; var response = new { Message = $"Our chain {message}", Chain = _chain }; return JsonConvert.SerializeObject(response); } internal int CreateTransaction(string sender, string recipient, int amount) { var transaction = new Transaction { Sender = sender, Recipient = recipient, Amount = amount }; _currentTransactions.Add(transaction); return _lastBlock != null ? _lastBlock.Index + 1 : 0; } } }