Last active
August 30, 2022 17:00
-
-
Save theahmadzai/fb9528212b723c13763aa87e9fe0cf78 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Security.Cryptography; | |
namespace BlockChain | |
{ | |
public interface IBlock | |
{ | |
byte[] Data { get; } | |
byte[] Hash { get; set; } | |
int Nonce { get; set; } | |
byte[] PrevHash { get; set; } | |
DateTime TimeStamp { get; } | |
} | |
public class Block : IBlock | |
{ | |
public byte[] Data { get; } | |
public byte[] Hash { get; set; } | |
public int Nonce { get; set; } | |
public byte[] PrevHash { get; set; } | |
public DateTime TimeStamp { get; } | |
public Block(byte[] data) | |
{ | |
Data = data ?? throw new ArgumentNullException(nameof(data)); | |
Nonce = 0; | |
PrevHash = new byte[] { 0x00 }; | |
TimeStamp = DateTime.Now; | |
} | |
public override string ToString() | |
{ | |
return $"{BitConverter.ToString(Hash).Replace("-", "")}:\n{BitConverter.ToString(PrevHash).Replace("-", "")}\n{Nonce} {TimeStamp}"; | |
} | |
} | |
public class BlockChain : IEnumerable<IBlock> | |
{ | |
public List<IBlock> Blocks { get; set; } = new List<IBlock>(); | |
public int Count => Blocks.Count; | |
public IBlock this[int index] { | |
get => Blocks[index]; | |
set => Blocks[index] = value; | |
} | |
public byte[] Difficulty { get; } | |
public BlockChain(byte[] difficulty, IBlock genesis) | |
{ | |
Difficulty = difficulty; | |
genesis.Hash = genesis.MineHash(difficulty); | |
Blocks.Add(genesis); | |
} | |
public void Add(IBlock block) | |
{ | |
if(Blocks.LastOrDefault() != null) { | |
block.PrevHash = Blocks.LastOrDefault()?.Hash; | |
} | |
block.Hash = block.MineHash(Difficulty); | |
} | |
public IEnumerator<IBlock> GetEnumerator() | |
{ | |
return Blocks.GetEnumerator(); | |
} | |
IEnumerator IEnumerable.GetEnumerator() | |
{ | |
return GetEnumerator(); | |
} | |
} | |
public static class BlockChainExtension | |
{ | |
public static byte[] GenerateHash(this IBlock block) | |
{ | |
using(SHA256 sha = new SHA256Managed()) | |
using(MemoryStream ms = new MemoryStream()) | |
using(BinaryWriter bw = new BinaryWriter(ms)) { | |
bw.Write(block.Data); | |
bw.Write(block.Nonce); | |
bw.Write(block.TimeStamp.ToBinary()); | |
bw.Write(block.PrevHash); | |
return sha.ComputeHash(ms.ToArray()); | |
} | |
} | |
public static byte[] MineHash(this IBlock block, byte[] difficulty) { | |
if(difficulty == null) throw new ArgumentNullException(nameof(difficulty)); | |
byte[] hash = new byte[0]; | |
int d = difficulty.Length; | |
while(!hash.Take(2).SequenceEqual(difficulty)) { | |
block.Nonce += 1; | |
hash = block.GenerateHash(); | |
} | |
return hash; | |
} | |
public static bool IsValid(this IBlock block) { | |
var bk = block.GenerateHash(); | |
return block.Hash.SequenceEqual(bk); | |
} | |
public static bool IsValid(this IEnumerable<IBlock> blocks) { | |
var blocksList = blocks.ToList(); | |
return blocksList.Zip(blocksList.Skip(1), Tuple.Create).All(block => block.Item2.IsValid()); | |
} | |
public static bool IsValidPrevBlock(this IBlock block, IBlock prevBlock) { | |
if(prevBlock == null) throw new ArgumentNullException(nameof(prevBlock)); | |
var prev = prevBlock.GenerateHash(); | |
return prevBlock.IsValid() && block.PrevHash.SequenceEqual(prev); | |
} | |
} | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
Random rnd = new Random(DateTime.UtcNow.Millisecond); | |
IBlock genesis = new Block(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00 }); | |
byte[] difficulty = new byte[] { 0x00, 0x00 }; | |
BlockChain chain = new BlockChain(difficulty, genesis); | |
for (int i =0; i<200; i++) { | |
var data = Enumerable.Range(0, 2256).Select(p => (byte)rnd.Next()); | |
chain.Add(new Block(data.ToArray())); | |
Console.WriteLine(chain.LastOrDefault()?.ToString()); | |
Console.WriteLine($"chain is valid: {chain.IsValid()}"); | |
} | |
Console.ReadKey(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment