public static class Constants { public const st…

Twetch ·

public static class Constants
{
public const string ABACUS_DATADIR = "%ABACUS_DATADIR%";
public const int MAGIC_NUMBER = 0x07770777;
}

public static class Program
{
public static void Main()
{
BlockGenerator<string>.Generate().Dump();
}
}

public interface IBlock<T>
{
long GetHeight();
string GetHash();
T SetData(T input);
T GetData();
}

public struct Block<T> : IBlock<T>
{
public int MagicNumber => Constants.MAGIC_NUMBER;
public long Height;
public long Difficulty;
public string Hash;
public string PreviousBlockHash;
public double Nonce;
public DateTime Timestamp;
public T Data;

public long GetHeight() => this.Height;
public string GetHash() => this.Hash;
public T GetData() => this.Data;
public T SetData(T input) => this.Data = input;
}

public class Blockchain<T> : List<Block<T>> { }

public static class BlockGenerator<T>
{
public static IObservable<IBlock<T>> Generate()
{
// Generates the blockchain.
Func<IObservable<IBlock<T>>,
Func<IObservable<IBlock<T>>, IObservable<IBlock<T>>>, IObservable<IBlock<T>>>
generateBlockchain =
(genesis, function) => Observable.Create<IBlock<T>>(observer =>
{
var subject = new Subject<IBlock<T>>();
var loopFunc = function(subject.ObserveOn(NewThreadScheduler.Default));
var loopSub = loopFunc.Subscribe(i => subject.OnNext(i));
var outerSub = subject.Subscribe(i => observer.OnNext(i));
genesis.Subscribe(subject.OnNext);
return new CompositeDisposable(2) { loopSub, outerSub };
});

Func<long> calculateDifficulty = () => 4; // TODO: actually calculate it.

Func<long, long, string, T, double,
(byte[], double)>
calculateHash = (height, difficulty, previousBlockHash, data, nonce) =>
{
var sha256 = System.Security.Cryptography.SHA256.Create();
sha256.Initialize();

var toHash = BitConverter.GetBytes(Constants.MAGIC_NUMBER)
.Union(BitConverter.GetBytes(height))
.Union(BitConverter.GetBytes(difficulty))
.Union(previousBlockHash?.HexToBytes() ?? BitConverter.GetBytes(0))
.Union(data?.Serialize() ?? default(T)?.Serialize() ?? 0.Serialize());

byte[] hash;
string hexHash;
do
{
var toHashPlusNonce = toHash.Union(BitConverter.GetBytes(nonce++));
hash = sha256.ComputeHash(toHashPlusNonce.ToArray());
hexHash = hash.BytesToHex();

} while (!hexHash.StartsWith("0".Repeat(calculateDifficulty())));

return (hash, nonce);
};

Func<double> getInitialNonce = () => new System.Random().NextDouble();

Func<IBlock<T>, T,
IBlock<T>>
createBlock = (previousBlock, data) =>
{
var block = new Block<T>();
block.Height = previousBlock.GetHeight() + 1;
block.Difficulty = calculateDifficulty();
block.PreviousBlockHash = previousBlock.GetHash();
block.Data = data;
var (hash, nonce) = calculateHash(block.Height, block.Difficulty, block.PreviousBlockHash, block.Data, getInitialNonce());
block.Hash = hash.BytesToHex();
block.Nonce = nonce;
block.Timestamp = DateTime.UtcNow;
return block;
};

Func<IObservable<IBlock<T>>>
createGenesisChain = () =>
{
var genesis = createBlock(default(Block<T>), default(T));
var exodus = createBlock(genesis, default(T));
return Observable.Return(genesis).Concat(Observable.Return(exodus)).Dump();
};

Func<IObservable<IBlock<T>>>
loadGenesisChain = () => null; // TODO: Implement loading from filesystem/db.

Func<T> getData = () => default(T); // TODO: Implement.

var blockchain = generateBlockchain(loadGenesisChain() ?? createGenesisChain(),
chain => chain.Zip(chain.Skip(1), (current, previous) => createBlock(previous, getData())));

return blockchain;
}
}

public static class ByteAndHexExtensions
{
//TODO: Validate string input, and deal with upper and lowercase variants.
//TODO: Optimize.
public static byte[] HexToBytes(this string input)
{
input = input.Replace("-", "");
var outputLength = input.Length / 2;
var output = new byte[outputLength];
for (var i = 0; i < outputLength; i++)
output[i] = Convert.ToByte(input.Substring(i * 2, 2), 16);
return output;
}

public static string BytesToHex(this byte[] input…