使用C#編寫(xiě)自己的區(qū)塊鏈挖礦算法
什么是加密貨幣挖掘?
一個(gè)加密貨幣的價(jià)值體現(xiàn)在它的稀缺性上,如果任何人都可以任意構(gòu)造一個(gè)比特幣,那么比特幣就毫無(wú)價(jià)值,所以比特幣的區(qū)塊鏈會(huì)讓參與者完成一項(xiàng)“工作”,根據(jù)這個(gè)工作的最終結(jié)果還分發(fā)比特幣,這個(gè)過(guò)程就被叫做“挖礦”。這就類似于一個(gè)黃金礦工花一些時(shí)間來(lái)工作,然后獲得一點(diǎn)黃金。
挖礦的原理
如果你百度/谷歌搜索 比特幣挖礦的原理 的話,都會(huì)給你說(shuō)是計(jì)算一個(gè)復(fù)雜的數(shù)學(xué)問(wèn)題而已,但是這么說(shuō)的話太籠統(tǒng)而且也太簡(jiǎn)單。采礦引擎如何工作這是一個(gè)重要的知識(shí)點(diǎn),所以我們需要了解一些密碼學(xué)知識(shí)和哈希算法相關(guān)的知識(shí),才能知道挖礦的基本原理。
哈希/散列介紹
單向加密人類能夠理解的輸入,例如 Hello World ,并將其扔到某個(gè)加密函數(shù)(即所謂的復(fù)雜的數(shù)學(xué)問(wèn)題),加密函數(shù)的算法越復(fù)雜,逆向工程就越困難。
例如一個(gè) SHA - 256 的例子,這個(gè)網(wǎng)站(鏈接: http://tool.oschina.net/encrypt?type=2 )可以很快的計(jì)算散列值,讓我們來(lái)散列 “Hello World” 看看會(huì)得到什么結(jié)果:

不管你試驗(yàn)幾次都會(huì)得到一樣的散列值,在編程中這種被稱之為冪等性。
加密算法的一個(gè)基本特性就是,它們很難通過(guò)逆向工程來(lái)得到明文結(jié)果,但是十分容易驗(yàn)證他們的加密結(jié)果,例如這里的 “Hello World” 很難通過(guò)逆向工程得到他的原明文結(jié)果,比特幣采用的是 Double SHA-256 也就是將明文通過(guò) SHA-256 計(jì)算過(guò)一次之后,再拿 SHA-256 針對(duì)散列值再次進(jìn)行計(jì)算,在這里我們只使用 SHA-256 來(lái)進(jìn)行加密。
工作證明
比特幣通過(guò)讓參與者散列隨機(jī)的字母與數(shù)字的組合,直到計(jì)算出來(lái)的散列包含前導(dǎo) 0。
例如我們計(jì)算 886 的散列值可以得到如下結(jié)果:
000f21ac06aceb9cdd0575e82d0d85fc39bed0a7a1d71970ba1641666a44f530
它返回了 3 個(gè) 0 作為前綴的散列值,但是我們?cè)趺粗?886 計(jì)算出來(lái)的散列結(jié)果產(chǎn)生了 3 個(gè) 0呢?
答案是我并不需要知道。我需要知道礦工給我的散列值前導(dǎo)有幾個(gè)零就好了,并不需要復(fù)雜的算法來(lái)驗(yàn)證整個(gè)散列值的有效性。
比特幣則稍微復(fù)雜一點(diǎn),它每隔 10 分鐘生成一個(gè)新的區(qū)塊,新區(qū)塊的散列值的難度它可以動(dòng)態(tài)調(diào)整,就類似于 CLR 的 GC 一樣,它可以根據(jù)目前挖礦的人數(shù)來(lái)進(jìn)行難度動(dòng)態(tài)調(diào)整,如果挖礦的人多的話,則調(diào)高難度,少則調(diào)低。
動(dòng)手開(kāi)發(fā)
1.項(xiàng)目配置
首先新建一個(gè) Asp.Net Core 項(xiàng)目,然后選擇 Empty Project(空項(xiàng)目) 類型,建立完成后無(wú)需進(jìn)行任何配置。
2.數(shù)據(jù)模型
這里我們來(lái)創(chuàng)建一個(gè)具體的區(qū)塊數(shù)據(jù)模型,使用的是 Struct 結(jié)構(gòu)體。
public struct Block
{
/// <summary>
/// 區(qū)塊位置
/// </summary>
public int Index { get; set; }
/// <summary>
/// 區(qū)塊生成時(shí)間戳
/// </summary>
public string TimeStamp { get; set; }
/// <summary>
/// 心率數(shù)值
/// </summary>
public int BPM { get; set; }
/// <summary>
/// 區(qū)塊 SHA-256 散列值
/// </summary>
public string Hash { get; set; }
/// <summary>
/// 前一個(gè)區(qū)塊 SHA-256 散列值
/// </summary>
public string PrevHash { get; set; }
/// <summary>
/// 下一個(gè)區(qū)塊生成難度
/// </summary>
public int Difficulty { get; set; }
/// <summary>
/// 隨機(jī)值
/// </summary>
public string Nonce { get; set; }
}
Difficulty 是一個(gè)整形,他定義了我們希望得到哈希前導(dǎo) 0 的數(shù)量,前導(dǎo) 0 越多,生成正確的散列值就越困難,我們現(xiàn)在從 1 開(kāi)始。
Nonce 則是每次計(jì)算塊散列值所需要的隨機(jī)值。
3. 工作證明
我們首先添加一個(gè)新的方法來(lái)驗(yàn)證生成的散列值是否包含指定數(shù)量的前導(dǎo) 0 :
/// <summary>
/// 校驗(yàn) Hash 是否有效
/// </summary>
/// <param name="hashStr">Hash 值</param>
/// <param name="difficulty">難度</param>
/// <returns></returns>
public static bool IsHashValid(string hashStr, int difficulty)
{
var bytes = Enumerable.Range(0, hashStr.Length)
.Where(n => n % 2 == 0)
.Select(n => Convert.ToByte(hashStr.Substring(n, 2), 16))
.ToArray();
var bits = new BitArray(bytes);
for (var i = 0; i < difficulty; i++)
{
if (bits[i]) return false;
}
return true;
}
然后我們更改了之前區(qū)塊 Hash 的生成方法:
/// <summary>
/// 計(jì)算區(qū)塊 HASH 值
/// </summary>
/// <param name="block">區(qū)塊實(shí)例</param>
/// <returns>計(jì)算完成的區(qū)塊散列值</returns>
public static string CalculateHash(Block block)
{
string calculationStr = $"{block.Index}{block.TimeStamp}{block.BPM}{block.PrevHash}{block.Nonce}";
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();
}
在這里我們新增新增了 Nonce 隨機(jī)值作為散列生成的依據(jù)。
那么我們生成新區(qū)塊的時(shí)候就順便來(lái)挖礦吧:
/// <summary>
/// 生成新的區(qū)塊
/// </summary>
/// <param name="oldBlock">舊的區(qū)塊數(shù)據(jù)</param>
/// <param name="BPM">心率</param>
/// <returns>新的區(qū)塊</returns>
public static Block GenerateBlock(Block oldBlock, int BPM)
{
Block newnewBlock = new Block()
{
Index = oldBlock.Index + 1,
TimeStamp = CalculateCurrentTimeUTC(),
BPMBPM = BPM,
PrevHash = oldBlock.Hash,
DifficultyDifficulty = Difficulty
};
// 挖礦 ing...
for (int i = 0; ; i++)
{
newBlock.Nonce = i.ToString("x2");
if (!IsHashValid(CalculateHash(newBlock), Difficulty))
{
Console.WriteLine($"目前結(jié)果:{CalculateHash(newBlock)} ,正在計(jì)算中...");
Task.Delay(1);
continue;
}
else
{
Console.WriteLine($"目前結(jié)果:{CalculateHash(newBlock)} ,計(jì)算完畢...");
newBlock.Hash = CalculateHash(newBlock);
break;
}
}
// 原有代碼
// newBlock.Hash = CalculateHash(newBlock);
return newBlock;
}
效果


結(jié)語(yǔ)
其實(shí)代碼并不復(fù)雜,但是這幾十行代碼表明了區(qū)塊鏈挖礦的本質(zhì),后面你可以參考原文實(shí)現(xiàn) P2P 與 股權(quán)權(quán)益證明方法與智能合約。
總結(jié)
以上所述是小編給大家介紹的使用C#編寫(xiě)自己的區(qū)塊鏈挖礦算法,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
如果你覺(jué)得本文對(duì)你有幫助,歡迎轉(zhuǎn)載,煩請(qǐng)注明出處,謝謝!
相關(guān)文章
MessageBox的Buttons和三級(jí)聯(lián)動(dòng)效果
這篇文章主要介紹了MessageBox的Buttons和三級(jí)聯(lián)動(dòng)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11
C#操作數(shù)據(jù)庫(kù)中存取圖片文件的方法
這篇文章主要介紹了C#操作數(shù)據(jù)庫(kù)中存取圖片文件的方法,以實(shí)例形式分析了C#將圖片存入數(shù)據(jù)庫(kù)及從數(shù)據(jù)庫(kù)讀取圖片文件的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10
動(dòng)態(tài)webservice調(diào)用接口并讀取解析返回結(jié)果
webservice的 發(fā)布一般都是使用WSDL(web service descriptive language)文件的樣式來(lái)發(fā)布的,在WSDL文件里面,包含這個(gè)webservice暴露在外面可供使用的接口。今天我們來(lái)詳細(xì)討論下如何動(dòng)態(tài)調(diào)用以及讀取解析返回結(jié)果2015-06-06
淺析C#中數(shù)組,ArrayList與List對(duì)象的區(qū)別
在C#中,當(dāng)我們想要存儲(chǔ)一組對(duì)象的時(shí)候,就會(huì)想到用數(shù)組,ArrayList,List這三個(gè)對(duì)象了。那么這三者到底有什么樣的區(qū)別呢2013-07-07
C#常用數(shù)據(jù)結(jié)構(gòu)之?dāng)?shù)組Array
這篇文章介紹了C#常用數(shù)據(jù)結(jié)構(gòu)之?dāng)?shù)組Array,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05

