文章原文来自:Code your own blockchain mining algorithm in Go! ,原始文章通过 Go 语言来实现的,这里仍然是承接上一篇文章,来使用 C# + .Net Core 实现我们的挖矿算法。

强烈建议阅读前先阅读这篇文章

什么是加密货币挖掘?

一个加密货币的价值体现在它的稀缺性上,如果任何人都可以任意构造一个比特币,那么比特币就毫无价值,所以比特币的区块链会让参与者完成一项“工作”,根据这个工作的最终结果还分发比特币,这个过程就被叫做“挖矿”。这就类似于一个黄金矿工花一些时间来工作,然后获得一点黄金。

挖矿的原理

如果你百度/谷歌搜索 比特币挖矿的原理 的话,都会给你说是计算一个复杂的数学问题而已,但是这么说的话太笼统而且也太简单。采矿引擎如何工作这是一个重要的知识点,所以我们需要了解一些密码学知识和哈希算法相关的知识,才能知道挖矿的基本原理。

哈希/散列介绍

单向加密人类能够理解的输入,例如 Hello World ,并将其扔到某个加密函数(即所谓的复杂的数学问题),加密函数的算法越复杂,逆向工程就越困难。

例如一个 SHA - 256 的例子,这个网站可以很快的计算散列值,让我们来散列 “Hello World” 看看会得到什么结果:

不管你试验几次都会得到一样的散列值,在编程中这种被称之为幂等性

加密算法的一个基本特性就是,它们很难通过逆向工程来得到明文结果,但是十分容易验证他们的加密结果,例如这里的 “Hello World” 很难通过逆向工程得到他的原明文结果,比特币采用的是 Double SHA-256 也就是将明文通过 SHA-256 计算过一次之后,再拿 SHA-256 针对散列值再次进行计算,在这里我们只使用 SHA-256 来进行加密。

工作证明

比特币通过让参与者散列随机的字母与数字的组合,直到计算出来的散列包含前导 0

例如我们计算 886 的散列值可以得到如下结果:

000f21ac06aceb9cdd0575e82d0d85fc39bed0a7a1d71970ba1641666a44f530

它返回了 30 作为前缀的散列值,但是我们怎么知道 886 计算出来的散列结果产生了 30 呢?

答案是我并不需要知道。。。我需要知道矿工给我的散列值前导有几个零就好了,并不需要复杂的算法来验证整个散列值的有效性。

比特币则稍微复杂一点,它每隔 10 分钟生成一个新的区块,新区块的散列值的难度它可以动态调整,就类似于 CLR 的 GC 一样,它可以根据目前挖矿的人数来进行难度动态调整,如果挖矿的人多的话,则调高难度,少则调低。

动手开发

1.项目配置

首先新建一个 Asp.Net Core 项目,然后选择 Empty Project(空项目) 类型,建立完成后无需进行任何配置。

2.数据模型

这里我们来创建一个具体的区块数据模型,使用的是 Struct 结构体。

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; }
/// <summary>
/// 下一个区块生成难度
/// </summary>
public int Difficulty { get; set; }
/// <summary>
/// 随机值
/// </summary>
public string Nonce { get; set; }
}

Difficulty 是一个整形,他定义了我们希望得到哈希前导 0 的数量,前导 0 越多,生成正确的散列值就越困难,我们现在从 1 开始。

Nonce 则是每次计算块散列值所需要的随机值。

3. 工作证明

我们首先添加一个新的方法来验证生成的散列值是否包含指定数量的前导 0 :

/// <summary>
/// 校验 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;
}

然后我们更改了之前区块 Hash 的生成方法:

/// <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}{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 随机值作为散列生成的依据。

那么我们生成新区块的时候就顺便来挖矿吧:

/// <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,
Difficulty = Difficulty
}; // 挖矿 ing...
for (int i = 0; ; i++)
{
newBlock.Nonce = i.ToString("x2");
if (!IsHashValid(CalculateHash(newBlock), Difficulty))
{
Console.WriteLine($"目前结果:{CalculateHash(newBlock)} ,正在计算中...");
Task.Delay(1);
continue;
}
else
{
Console.WriteLine($"目前结果:{CalculateHash(newBlock)} ,计算完毕...");
newBlock.Hash = CalculateHash(newBlock);
break;
}
} // 原有代码
// newBlock.Hash = CalculateHash(newBlock);
return newBlock;
}

效果

结语

其实代码并不复杂,但是这几十行代码表明了区块链挖矿的本质,后面你可以参考原文实现 P2P 与 股权权益证明方法与智能合约。

项目代码地址:http://git.myzony.com/Zony/BlockChain.git

PDF 文件地址:http://git.myzony.com/Zony/BlockChain/src/6ef2bdb7d9a086d8e214603c88670896de6b108c/PDF 文件/[C%23] 使用 C%23 编写自己区块链的挖矿算法.pdf

Code a simple P2P blockchain in Go.

[C#]使用 C# 编写自己的区块链挖矿算法的更多相关文章

  1. [转帖][区块链]共识算法(POW,POS,DPOS,PBFT)介绍和心得

    [区块链]共识算法(POW,POS,DPOS,PBFT)介绍和心得 置顶 2017-03-12 18:31:19 乐扣老师lekkoliu 阅读数 127953  收藏 更多 分类专栏: 技术管理 区 ...

  2. 【原】用Java编写第一个区块链(一)

    写这篇随笔主要是尝试帮助自己了解如何学习区块链技术开发. [本文禁止任何形式的全文粘贴式转载,本文来自 zacky31 的随笔] 目标: 创建一个最基本的"区块链" 实现一个简单的 ...

  3. 用Java编写第一个区块链

    原文地址:https://www.cnblogs.com/zacky31/p/9057193.html 目标: 创建一个最基本的“区块链” 实现一个简单的挖矿系统 前提: 对面向对象编程有一定的基础 ...

  4. 【原】用Java编写第一个区块链(二)

    这篇文章将去介绍如何使用区块链进行交易. [本文禁止任何形式的全文粘贴式转载,本文来自 zacky31 的随笔] 目标: 在上一篇文章中,我们已经创建了一个可信任的区块链.但是目前所创建的链中包含的有 ...

  5. [区块链] 共识算法之争(PBFT,Raft,PoW,PoS,DPoS,Ripple)

    近几天对区块链中几种常见的共识机制(PBFT,Raft,PoW,PoS,DPoS,Ripple)进行了总结.尽量使用简单易懂语言,篇幅较大,想了解的可以只读每个算法介绍中前边的原理.本篇文章主要参考& ...

  6. 区块链共识算法 PBFT(拜占庭容错)、PAXOS、RAFT简述

    共识算法 区块链中最重要的便是共识算法,比特币使用的是POS(Proof of Work,工作量证明),以太币使用的是POS(Proof of Stake,股权证明)使得算理便的不怎么重要了,而今PO ...

  7. 区块链共识算法|RAFT和PBFT的区别

    这里有个很形象的比喻: 一个团队一定会有一个老大和普通成员.对于 raft 算法,共识过程就是:只要老大还没挂,老大说什么,我们(团队普通成员)就做什么,坚决执行.那什么时候重新老大呢?只有当老大挂了 ...

  8. 未来-区块链-Micron:区块链永远不会忘记:内存对这项革命性技术的推动作用

    ylbtech-未来-区块链-Micron:区块链永远不会忘记:内存对这项革命性技术的推动作用 1.返回顶部 1. 俗话说,大象永远不会忘记.区块链亦是如此. 内存是区块链的核心,它是一种以关键方式构 ...

  9. 只用120行Java代码写一个自己的区块链-2网络

    已经看完第一章的内容了吗,欢迎回来. 上一章我们介绍了关于怎么去编写自己的区块链,完成哈希和新块的校验.但是它只是在一个终端(结点)上跑.我们怎么样来连接其他结点以及贡献新的块呢,怎么样广播到其他结点 ...

随机推荐

  1. Eclipse中pydev开发,python重载__init__报错,是可以正常执行

    python支持函数重载 而在使用Eclipse的pydev开发时,重载函数__init__时,却出现了错误duplicated 我想不到解决办法,但是居然能正常执行. pydev报错是编译错误,而p ...

  2. OC可点击的两种轮播图效果

    基本上,每一个APP都有一个轮播图的效果展示,一般都是用来展示图片的一些信息,然后可以点击查看或购买,所以在此我将这种轮播图进行了一个类的封装,效果包含两种形式:第一种,来回轮转样式,第二种,一个方向 ...

  3. java实现:将一个数各个位数相加

    前面已经实现过这个程序,现在我们就不多说了,直接更改C的源码,实现这个JAVA程序. import java.util.Scanner; public class HelloWorld { publi ...

  4. C++实现最小堆及插入,调整顺序,删除堆顶元素的操作

    上次用Java实现了最大堆的封装,这次就来写一下最小堆的实现吧 插入函数的思路: 向堆中插入元素有两种情况,一种是堆为空,那么就让插入值作为根节点即可:另一种是堆不为空,那么此时就要进行判断当前节点与 ...

  5. python字符串与数字类型转化

    数字转字符串:str(数字),如str(10) 相反:int(字符串),如int('10') 另外,import string后 用string.atoi('100',base),转换为int,bas ...

  6. 【Android 应用开发】Android 图表绘制 achartengine 示例解析

    作者 : 韩曙亮 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/38420197 一. AChartEngine 简介 1. 项 ...

  7. [问与答]为什么 'a' in ('abc') 是True 而 'a' in ['abc'] 是False呢?

    Why is 'a' in ('abc') True while 'a' in ['abc'] is False? 原文链接 问 在使用解释器的时候,表达式'a' in ('abc') 返回是True ...

  8. 文件I/O实践(3) --文件共享与fcntl

    文件共享 一个进程打开了两个文件 文件表条目(file-table-entry): 1.文件状态标志(file-status-flags): 读/写/追加/同步/非阻塞等; 2.当前文件偏移量 3.v ...

  9. H5学习之旅-H5的布局(10)

    两种实现方式:div和table div实现布局的方式 代码实例 <!DOCTYPE html> <html lang="en"> <head> ...

  10. python 去掉 pyc

    python 去掉 .pyc 在开发的机器上(Ubuntu),python自动生成的pyc文件太影响心情,把下面的语句添加到 /etc/profile中: # do not produce .pyc ...