C# 簡單的區塊鏈實現

1.項目配置

首先新建一個 Asp.Net Core 項目,而後選擇 Empty Project(空項目) 類型,創建完成後無需進行任何配置。html

2.數據模型

這裏咱們來建立一個具體的區塊數據模型,使用的是 Struct 結構體。web

public struct Block
{
    /// <summary>
    /// 區塊位置
    /// </summary>
    public int Index { get; set; }
    /// <summary>
    /// 區塊生成時間戳
    /// </summary>
    public string TimeStamp { get; set; }
    /// <summary>
    /// 心率數值
    /// </summary>
    public int BPM { get; set; }
    /// <summary>
    /// 區塊 SHA-256 散列值
    /// </summary>
    public string Hash { get; set; }
    /// <summary>
    /// 前一個區塊 SHA-256 散列值
    /// </summary>
    public string PrevHash { get; set; }
}

這裏各個字段的含義已經在註釋上方標明瞭,這裏不在過多贅述。
以後咱們新建一個 BlockGenerator 靜態類用於管理區塊鏈,而且使用一個 List 保存區塊鏈數據。算法

public static class BlockGenerator
{
    public static List<Block> _blockChain = new List<Block>();
}

咱們使用散列算法(SHA256)來肯定和維護鏈中塊和塊正確的順序,確保每個塊的 PrevHash 值等於前一個塊中的 Hash 值,這樣就以正確的塊順序構建出鏈:
img1markdown

4.散列與生成區塊

使用散列是由於可使用極少的控件生成每個區塊的惟一標識,並且能夠維持整個區塊鏈的完整性,經過每一個區塊存儲的前一個鏈的散列值,咱們就能夠確保區塊鏈當中每個區塊的正確性,任何針對區塊的無效更改都會致使散列值的改變,也就破壞了區塊鏈。
那麼咱們就在 BlockGenerator 當中添加一個函數用於計算 Block 的 Hash 值:app

/// <summary>
/// 計算區塊 HASH 值
/// </summary>
/// <param name="block">區塊實例</param>
/// <returns>計算完成的區塊散列值</returns>
public static string CalculateHash(Block block)
{
    string calculationStr = $"{block.Index}{block.TimeStamp}{block.BPM}{block.PrevHash}";

    SHA256 sha256Generator = SHA256.Create();
    byte[] sha256HashBytes = sha256Generator.ComputeHash(Encoding.UTF8.GetBytes(calculationStr));

    StringBuilder sha256StrBuilder = new StringBuilder();
    foreach (byte @byte in sha256HashBytes)
    {
        sha256StrBuilder.Append(@byte.ToString("x2"));
    }

    return sha256StrBuilder.ToString();
}

這裏的 CalculateHash 函數接收一個 Block 實例,經過該實例當中的 Index、TimeStamp、BPM、PrevHash 的值來計算出當前塊的 SHA256 Hash 值,以後咱們就能夠來編寫一個生成塊的函數:async

/// <summary>
/// 生成新的區塊
/// </summary>
/// <param name="oldBlock">舊的區塊數據</param>
/// <param name="BPM">心率</param>
/// <returns>新的區塊</returns>
public static Block GenerateBlock(Block oldBlock, int BPM)
{
    Block newBlock = new Block()
    {
        Index = oldBlock.Index + 1,
        TimeStamp = CalculateCurrentTimeUTC(),
        BPM = BPM,
        PrevHash = oldBlock.Hash
    };

    newBlock.Hash = CalculateHash(newBlock);
    return newBlock;
}

這個函數須要接收前一個塊對象的值,用於新區塊的 Index 遞增以及 新的 SHA256 Hash 計算。
這裏摻入了一個 CalculateCurrentTimeUTC 函數,該函數主要是用於將 DateTime.Now 時間轉換爲 UTC 時間,以下:函數

/// <summary>
/// 計算當前時間的 UTC 表示格式
/// </summary>
/// <returns>UTC 時間字符串</returns>
public static string CalculateCurrentTimeUTC()
{
    DateTime startTime = new DateTime(1970, 1, 1, 0, 0, 0, 0);
    DateTime nowTime = DateTime.Now;

    long unixTime = (long)Math.Round((nowTime - startTime).TotalMilliseconds, MidpointRounding.AwayFromZero);
    return unixTime.ToString();
}

5.校驗區塊

每個區塊都是不可信的,因此咱們須要在生成新的區塊的時候對其進行校驗,校驗規則以下:post

  • 校驗新區塊與舊區塊的 Index 是否正確遞增
  • 校驗新區塊的 Hash 值是否正確
  • 校驗新區塊的 PrevHash 值是否與舊區塊的 Hash 值匹配

有了上述幾種條件,咱們能夠編寫一個校驗函數以下:區塊鏈

/// <summary>
/// 檢驗區塊是否有效
/// </summary>
/// <param name="newBlock">新生成的區塊數據</param>
/// <param name="oldBlock">舊的區塊數據</param>
/// <returns>有效返回 TRUE,無效返回 FALSE</returns>
public static bool IsBlockValid(Block newBlock, Block oldBlock)
{
    if (oldBlock.Index + 1 != newBlock.Index) return false;
    if (oldBlock.Hash != newBlock.PrevHash) return false;
    if (CalculateHash(newBlock) != newBlock.Hash) return false;

    return true;
}

除開區塊校驗的問題以外,若是有兩個節點被分別添加到各自的區塊鏈上,咱們應該始終以最長的那一條爲主線,由於最長的那一條意味着他的區塊數據始終是最新的。ui

img2
So,咱們還須要一個更新最新區塊的函數:

/// <summary>
/// 若是新的區塊鏈比當前區塊鏈更新,則切換當前區塊鏈爲最新區塊鏈
/// </summary>
/// <param name="newBlockChain">新的區塊鏈</param>
public static void SwitchChain(List<Block> newBlockChain)
{
    if (newBlockChain.Count > _blockChain.Count)
    {
        _blockChain = newBlockChain;
    }
}

6.集成到 Web 當中

如今整個區塊鏈的基本操做已經完成,如今咱們須要讓他運轉起來,咱們來到 StartUp 當中,添加兩個新的路由:

app.Map("/BlockChain", _ =>
{
    _.Run(async context =>
    {
        if (context.Request.Method == "POST")
        {
            // 增長區塊鏈
            if (BlockGenerator._blockChain.Count == 0)
            {
                Block firstBlock = new Block()
                {
                    Index = 0,
                    TimeStamp = BlockGenerator.CalculateCurrentTimeUTC(),
                    BPM = 0,
                    Hash = string.Empty,
                    PrevHash = string.Empty
                };

                BlockGenerator._blockChain.Add(firstBlock);

                await context.Response.WriteAsync(JsonConvert.SerializeObject(firstBlock));
            }
            else
            {
                int.TryParse(context.Request.Form["BPM"][0], out int bpm);

                Block oldBlock = BlockGenerator._blockChain.Last();
                Block newBlock = BlockGenerator.GenerateBlock(oldBlock, bpm);

                if (BlockGenerator.IsBlockValid(newBlock, oldBlock))
                {
                    List<Block> newBlockChain = new List<Block>();
                    foreach (var block in BlockGenerator._blockChain)
                    {
                        newBlockChain.Add(block);
                    }

                    newBlockChain.Add(newBlock);
                    BlockGenerator.SwitchChain(newBlockChain);
                }

                await context.Response.WriteAsync(JsonConvert.SerializeObject(newBlock));
            }
        }
    });
});

app.Map("/BlockChains", _ =>
{
    _.Run(async context =>
    {
        await context.Response.WriteAsync(JsonConvert.SerializeObject(BlockGenerator._blockChain));
    });
});

7.最終效果

咱們先經過 PostMan 來構建一個創世塊:


img3
而後咱們嘗試多添加幾個以後,訪問 BlockChain 來查看已經存在的區塊鏈結構:
img4

8.結語

經過以上代碼咱們完成了一個簡陋的區塊鏈,雖然十分簡陋,可是已經具有了塊生成,散列計算,塊校驗這些基本能力,你能夠參考 GitHub 上面各類成熟的區塊鏈實現來完成工做量證實、權益證實這樣的共識算法,或者是智能合約、Dapp、側鏈等等。

 

 

原文地址:http://www.cnblogs.com/myzony/p/8478789.html

相關文章
相關標籤/搜索