C#200行代码实现区块链

来源:myzony

cnblogs.com/myzony/p/8478789.html

文章原文来自:Code your own blockchain in less than 200 lines of Go!

原始文章是通过 Go 语言来实现自己的区块链的,这里我们参照该文章来使用 C# + Asp.Net Core 实现自己的区块链。

1、项目配置

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

2、数据模型

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

public struct Block

{ ///

/// 区块位置

///

public int Index { get; set; } ///

/// 区块生成时间戳

///

public string TimeStamp { get; set; } ///

/// 心率数值

///

public int BPM { get; set; } ///

/// 区块 SHA-256 散列值

///

public string Hash { get; set; } ///

/// 前一个区块 SHA-256 散列值

///

public string PrevHash { get; set; }

}

这里各个字段的含义已经在注释上方标明了,这里不在过多赘述。

之后我们新建一个 BlockGenerator 静态类用于管理区块链,并且使用一个 List 保存区块链数据。

public static class BlockGenerator

{

public static List _blockChain = new List();

}

我们使用散列算法(SHA256)来确定和维护链中块和块正确的顺序,确保每一个块的 PrevHash 值等于前一个块中的 Hash 值,这样就以正确的块顺序构建出链:

3、散列与生成区块

使用散列是因为可以使用极少的控件生成每一个区块的唯一标识,而且可以维持整个区块链的完整性,通过每个区块存储的前一个链的散列值,我们就可以确保区块链当中每一个区块的正确性,任何针对区块的无效更改都会导致散列值的改变,也就破坏了区块链。

那么我们就在 BlockGenerator 当中添加一个函数用于计算 Block 的 Hash 值:

///

/// 计算区块 HASH 值

///

///

区块实例

/// 计算完成的区块散列值

public static string CalculateHash(Block block)

{

string calculationStr = $"";

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 值,之后我们就可以来编写一个生成块的函数:

///

/// 生成新的区块

///

///

旧的区块数据

///

心率

/// 新的区块

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 时间,如下:

///

/// 计算当前时间的 UTC 表示格式

///

/// UTC 时间字符串

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();

}

4、校验区块

每一个区块都是不可信的,所以我们需要在生成新的区块的时候对其进行校验,校验规则如下:

校验新区块与旧区块的 Index 是否正确递增

校验新区块的 Hash 值是否正确

校验新区块的 PrevHash 值是否与旧区块的 Hash 值匹配

有了上述几种条件,我们可以编写一个校验函数如下:

///

/// 检验区块是否有效

///

///

新生成的区块数据

///

旧的区块数据

/// 有效返回 TRUE,无效返回 FALSE

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;

}

除开区块校验的问题之外,如果有两个节点被分别添加到各自的区块链上,我们应该始终以最长的那一条为主线,因为最长的那一条意味着他的区块数据始终是最新的。

So,我们还需要一个更新最新区块的函数:

///

/// 如果新的区块链比当前区块链更新,则切换当前区块链为最新区块链

///

///

新的区块链

public static void SwitchChain(List newBlockChain)

{

if (newBlockChain.Count > _blockChain.Count)

{

_blockChain = newBlockChain;

}

}

5、集成到 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 newBlockChain = new List();

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));

});

});

6、最终效果

我们先通过 PostMan 来构建一个创世块:

然后我们尝试多添加几个之后,访问 BlockChain 来查看已经存在的区块链结构:

7、结语

通过以上代码我们完成了一个简陋的区块链,虽然十分简陋,但是已经具备了块生成,散列计算,块校验这些基本能力,你可以参考 GitHub 上面各种成熟的区块链实现来完成工作量证明、权益证明这样的共识算法,或者是智能合约、Dapp、侧链等等。

看完本文有收获?请转发分享给更多人

关注「DotNet」,提升.Net技能

淘口令:复制以下红色内容,再打开手淘即可购买

范品社,使用¥极客T恤¥抢先预览(长按复制整段文案,打开手机淘宝即可进入活动内容)

  • 发表于:
  • 原文链接:http://kuaibao.qq.com/s/20180509B0OOUT00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券