C# 实现区块链(附c#完整源码即运行截图)

参考文章:http://www.cnblogs.com/myzony/p/8478789.html

使用 C# + Asp.Net Core 实现自己的区块链项目。

1.项目配置

首先新建一个 Asp.Net Core 项目,然后选择 Empty Project(空项目) 类型,建立完成后无需进行任何配置。(本文以Microsoft Visual Studio2017企业版创建项目)

过程如下:

(1).打开文件→新建→项目

(2) 选择Visual C# 菜单栏下的Web →选择.NET.Core→选择ASP.NET.Core Web应用程序进行创建项目。

(3)选择空类型进行创建,勾选:为HTPS配置,创建完成后不需要进行任何配置。

 

2.Program.cs中需要的引用声明

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

 

 

3.建立区块链数据模型

(1)创建一个具体的区块数据模型,使用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; }
        }

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

代码如下:

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

            internal static string CalculateCurrentTimeUTC()
            {
                throw new NotImplementedException();
            }

            internal static void SwitchChain(List<Block> newBlockChain)
            {
                throw new NotImplementedException();
            }

            internal static Block GenerateBlock(Block oldBlock, int bpm)
            {
                throw new NotImplementedException();
            }

            internal static bool IsBlockValid(Block newBlock, Block oldBlock)
            {
                throw new NotImplementedException();
            }
        }

(3)在 BlockGenerator 当中添加一个函数用于计算 Block 的 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}";
            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();
        }

(4)这里的 CalculateHash 函数接收一个 Block 实例,通过该实例当中的 Index、TimeStamp、BPM、PrevHash 的值来计算出当前块的 SHA256 Hash 值,之后我们就可以来编写一个生成块的函数:

代码如下:

 /// <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;
        }

(5)这个函数需要接收前一个块对象的值,用于新区块的 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();
        }

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

  • 校验新区块与旧区块的 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;
        }

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

/// <summary>
        /// 如果新的区块链比当前区块链更新,则切换当前区块链为最新区块链
        /// </summary>
        /// <param name="newBlockChain">新的区块链</param>
        public static void SwitchChain(List<Block> newBlockChain)
        {
            if (newBlockChain.Count > _blockChain.Count)
            {
                _blockChain = newBlockChain;
            }
        }
 

 

3.集成到Web

在StartUp 当中,添加两个新的路由

Startup.cs源码如下(包含引用及路由)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using static WebApplication1.Program;

namespace WebApplication1
{
    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            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
                        {
                            var form = await context.Request.ReadFormAsync();
                            int.TryParse(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));
                });
            });
           
        }
    }
}
 

4.暂行调试结果:

 

5.最终效果

(1)先通过PostMan创建一个创世块

PostMan下载链接:https://www.getpostman.com/products,选择适用版本安装后创建一个创世块。

(2)然后尝试多添加几个之后,访问 BlockChain 来查看已经存在的区块链结构。

 

附c#项目代码:

 https://gitee.com/DiJun1/QuKuaiLianShiLi

 


版权声明:本文为charlottehe原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。