天天看點

.Net Core實作區塊鍊初探

區塊鍊這麼火,咱也跟個風。

一、前言

最近,銀行總行關于數字貨币即将推出的消息頻傳,把BTC也帶得來了一波反彈。

借着這個風,我們也研究一下區塊鍊。

通常大家說到區塊鍊,實際包括兩部分概念:

第一個概念,就是狹義上的區塊鍊。聽起來很高大上又很複雜,但追根到底,它就是一種加密應用。

提起加密,我們腦袋裡會顯現出:DES、3DES、AES、RSA、DSA、SHA-1、MD5……很多很多。

狹義的區塊鍊,其實就是使用這些加密技術而形成的一種應用。

這個應用又分為兩個部分:

  1. 區塊

區塊就是存放資料的一個獨立空間。

不好了解?舉個例子:我們在紙上寫個作文,寫了好幾篇。那每一篇紙上,都會有一些我們寫的内容。這個内容,就是資料。而這張紙,就是一個區塊。

是以,區塊就是放某些資料的一個特定的獨立的空間。

根據需要,一個資料可以放在一個區塊上,也可以放在多個區塊上。同時,一個區塊可以隻存放一個資料,也可以存放很多個資料。這兒不需要太糾結怎麼放,自己決定就好。區塊鍊關注的是資料的存放方式,而不是資料本身。

鍊這個概念更簡單,就是把上面說的區塊,用一個連結清單記錄下來。

既然說到連結清單,就能想到,在連結清單上記錄的區塊,是有次序的。此外,最重要的是,鍊裡的每一個區塊,在記錄資料的同時,也同時記錄了他的前一個區塊的資訊(在區塊鍊裡,稱之為指紋)。

換句話說就是,每個區塊裡,都記錄着這個區塊前邊所有區塊的資訊,同時,每個區塊,都對這個區塊後邊的所有區塊産生影響。

這樣的設計,會形成這樣的效果:當改變一個區塊的資料時,需要同時把這個區塊後邊所有區塊的指紋資訊全部進行同步更新。如果僅僅隻改變這個區塊本身的内容,那後邊的區塊會很容易通過指紋來驗證這個區塊非法和無效。

第二個概念,是分布式存儲

上面區塊鍊的概念中,在資料儲存上有一個漏洞:如果一個非法使用者真的把一個區塊以及這個區塊後邊的所有區塊都修改了,那他就改變了這個區塊鍊裡儲存的資料。怎麼破?

一個有效的方式,就是分布式存儲。把這樣一個鍊,存放在很多個地方,每個地方都有這個鍊的一個副本。系統驗證一個區塊是否合法,除了驗證鍊的合法性外,還需要驗證這個區塊在各處儲存的副本是否一緻。系統認可超過半數一緻的區塊為合法區塊。

這樣,非法使用者如果想改變一個區塊的資料,不僅需要修改這個鍊,同時還需要把這個鍊在各處的副本中半數以上的記錄也修改了。當這個副本的數量很大時,這将變成一個不可能完成的任務。

當然,在網際網路上,安全永遠是相對的。去年币圈最大的事件,就是真的有一幫子黑客,利用廉價的伺服器,造出了超過半數的區塊鍊副本,然後修改區塊資料,并讓這些超過半數的副本認可并覆寫了正常的區塊鍊資料,進而盜取了大量的數字币并抛售。

這是題外話。

區塊鍊的概念就說到這裡。

今天的代碼,我們僅研究區塊鍊的原理和方法。分布式存儲,有興趣的話,可以研究一下P2P的種子結構和下載下傳原理,路數是一樣的。

下面上代碼。

    為了防止不提供原網址的轉載,特在這裡加上原文連結:https://www.cnblogs.com/tiger-wang/p/12966882.html

二、開發環境&基礎工程

這個Demo的開發環境是:Mac + VS Code + Dotnet Core 3.1.2。

$ dotnet --info
.NET Core SDK (reflecting any global.json):
 Version:   3.1.201
 Commit:    b1768b4ae7

Runtime Environment:
 OS Name:     Mac OS X
 OS Version:  10.15
 OS Platform: Darwin
 RID:         osx.10.15-x64
 Base Path:   /usr/local/share/dotnet/sdk/3.1.201/

Host (useful for support):
  Version: 3.1.3
  Commit:  4a9f85e9f8

.NET Core SDKs installed:
  3.1.201 [/usr/local/share/dotnet/sdk]

.NET Core runtimes installed:
  Microsoft.AspNetCore.App 3.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 3.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
           

首先,在這個環境下建立工程:

  1. 建立Solution
% dotnet new sln -o demo
The template "Solution File" was created successfully.
           
  1. 這次,我們用Console建立工程
% cd demo
% dotnet new webapi -o demo
The template "Console Application" was created successfully.

Processing post-creation actions...
Running 'dotnet restore' on demo/demo.csproj...
  Determining projects to restore...
  Restored demo/demo.csproj (in 170 ms).

Restore succeeded.
           

基礎工程搭建完成。

三、建立區塊Model

在工程下面,建立一個目錄

Models

,并在目錄下建立類

Block.cs

public class Block
{
    public DateTimeOffset time_stamp { get; set; }
    public object data { get; set; }
    public string pre_hash { get; set; }
    public string hash { get; set; }
    public string nonce { get; set; }
}
           

解釋一下各個字段:

time_stamp:時間戳,也就是這個區塊的建立時間

data:資料,可以是任意類型,是我們要用區塊鍊來儲存的資料

pre_hash:前一個區塊的hash值

hash:目前區塊的hash值

nonce:随機數

在這幾個字段中,真正在區塊鍊中起作用的是後面三個字段:pre_hash、hash、nonce。

在計算中,hash值是由這個區塊的pre_hash、data、time_stamp、nonce四個字段共同計算産生。

這樣做,一方面,我們通過hash值可以驗證儲存的資料data,同時,也把前一個區塊的hash資訊儲存到了這個區塊中。

nonce字段在這個demo中,實際意義不大,但在實際項目中,卻有它的實用價值。比方BTC中,要求hash值有特定的格式(至少前8個位元組全是0),需要通過改變nonce的值,來得到這樣的hash。又因為hash無法逆向計算,是以隻能用窮舉法修改nonce,一個一個計算并測試hash,這個過程叫WK,

四、建立鍊

有了區塊model,建立鍊很簡單。

我們建立一個

BlockChains

類,并在裡面用

SortedList

建立一個鍊。

public class BlockChains
{
    private static SortedList<int, Block> _block_chains = new SortedList<int, Block>();
}
           

鍊也建完了。

後面,我們會在這個鍊中實作對于區塊鍊的各種處理方法。

五、往鍊中增加區塊

下面我們在

BlockChains

類中寫一個往鍊中增加區塊的方法:

private static string _hash_zero = "Initialize_Hash_By_WangPlus";

public bool addBlockData(object data)
{
    Block new_block = new Block()
    {
        time_stamp = DateTimeOffset.Now,
        data = data,
        nonce = $"{_random.Next(9999):D4}",
    };

    new_block.pre_hash = _block_chains.Count <= 0 ? _hash_zero : _block_chains.Last().Value.hash;
    new_block.hash = calculateHash(new_block);

    _block_chains.Add(_block_chains.Count + 1, new_block);

    return true;
}
private string calculateHash(Block block)
{
    if (block == null)
        return string.Empty;

    string data_json = JsonConvert.SerializeObject(block.data, Formatting.None);
    string block_string = $"{block.time_stamp.Ticks.ToString()}|{block.pre_hash}|{data_json}|{block.nonce}";

    var block_hash = new SHA256Managed().ComputeHash(Encoding.UTF8.GetBytes(block_string));

    return Convert.ToBase64String(block_hash);
}
           

在這個實作的方法中,

  1. 第一個區塊需要特殊處理,因為他的

    pre_hash

    不存在,是以我們給了一個預設的串。
  2. 計算

    hash

    時,我們把區塊的

    time_stamp

    pre_hash

    data

    nonce

    全都包含在裡面了。

六、驗證區塊

也是一個方法,加在

BlockChains

中:

public bool isBlockValid(int index)
{
    if (index <= 0 || index > _block_chains.Count)
        return false;

    if ((index > 1 && _block_chains[index].pre_hash != _block_chains[index - 1].hash) || (index == 1 && _block_chains[index].pre_hash != _hash_zero))
        return false;

    if (_block_chains[index].hash != calculateHash(index))
        return false;

    return true;
}
private string calculateHash(int index)
{
    return calculateHash(_block_chains[index]);
}
           

這個Demo中,沒有實作分布存儲,是以驗證區塊的部分,我們隻做了簡單驗證:驗證目前區塊和前一個區塊的hash是否比對。

做到這兒,這個簡單的區塊鍊Demo就完成了。

七、總結和思考

上面是一個簡單的區塊鍊應用中,區塊鍊概念的實作。

在實際應用中,我們需要了解以下内容:

  1. 區塊鍊是一個加密技術,它本身跟資料無關;
  2. 區塊鍊在形成後,是一個隻讀鍊,就是說在通常情況下,我們不會從一個鍊中修改或删除一個區塊。因為這會導緻後續所有區塊的修改,這個代價很大;
  3. 因為區塊不可更改,是以區塊中存儲的資料也不可更改。如果儲存的資料有錯,通常是采用類似記帳的方式,用反沖記錄去消除這個錯誤,而不是修改區塊鍊;
  4. 區塊鍊應用中,在安全級别要求比較高的情況下,應該把開發重點放在分布存儲上面。

上面Demo的代碼已傳到Github。

代碼位址:https://github.com/humornif/Demo-Code/tree/master/0010/demo

(全文完)

.Net Core實作區塊鍊初探

微信公衆号:老王Plus

掃描二維碼,關注個人公衆号,可以第一時間得到最新的個人文章和内容推送

本文版權歸作者所有,轉載請保留此聲明和原文連結

繼續閱讀