区块链(Blockchain),是比特币的一个重要概念,它本质上是一个去中心化的数据库,同时作为比特币的底层技术,是一串使用密码学方法相关联产生的数据块,每一个数据块中包含了一批次比特币网络交易的信息,用于验证其信息的有效性(防伪)和生成下一个区块。(百度百科)

       区块链本身的结构是非常简单的,其复杂的部分在于他的共识机制,加密等部分。我们可以将区块链看做一种数据结构,顾名思义,区块链就是把许多个区块(Block)链接在一起,成为一个链式结构(Chain)。所以我们要做的事情也很简单,就是首先创建出区块,然后把它们连起来。

1.区块的构建

       首先,要确定的是区块里面要存什么。由于我们只是要构建一个最基础的区块链,所以区块里面的信息也比较简单。我们只往里存两个属性,前一个节点的哈希值(reviousHash)和交易信息(transaction)

type Block struct {
previousHash string
transaction []string
}

       为了计算哈希值,我们要把Block结构体进行序列化。这里我选择使用protobuf,当然你也可以选择使用json或者直接转成[]byte。
       在block包下新建一个pb包,然后新建block.proto文件。里面要写的内容很简单,只有一个message的定义。

syntax="proto3";
package blockpb; message Block{
string previousHash = 1;
repeated string transaction = 2;
}

然后接下来编译proto文件,会自动生成block.pb.go文件(别忘了敲等号后面的小点)

protoc --go_out=. block.proto

有了block.pb.go,我们就可以很容易的计算每个区块的哈希值了。首先,写一个方法来把block转化为blockpb。

//把block转化为blockpb
func ToProto(block *Block) proto.Message {
return &blockpb.Block{
PreviousHash: block.previousHash,
Transaction: block.transaction,
}
}

接下来就是获取区块的哈希值,这个部分也很简单

func GetHash(block *Block) string{
serialBlock := ToProto(block)
byteBlock, _ := proto.Marshal(serialBlock)
hash := sha1.Sum(byteBlock)
return hex.EncodeToString(hash[:])
}

到这里所有关于区块的部分就已经结束了。接下来就是如何把这些区块给串起来,形成链式结构了。

2.区块的链接

       我们创建的是一个非常简单基础的链,所以接下来的操作都会在main.go的main方法里进行。很显然,既然对于每个节点,我们都能计算出一个hash值,并且这个hash值是唯一的。那么们就可以通过块的hash值来定位到这个块的后一个节点是谁,因为我们每个块除了包含交易信息以外,还保存了上一个区块的hash值。

  • 创世块的创建
    创世块(Genesis Block)是一条区块链第一个创建的区块。这个区块的特殊之处在于,他并没有上一个区块,也就是说这个块的previousHash是空的。我们就用空字符来初始化他的上一个区块哈希值。
genesisBlock := block.CreateBlock("",[]string{"Hello","BlockChain","World"})

       这个区块包含的交易信息是Hello BlockChain World。当然,在实际的区块中,这里会存放一些有意义的信息,而不是随手打的Hello World。

  • 其他区块的创建
    接下来创建其他区块,我们在这里就先创建三个块。注意,必须用上一个区块的哈希值来初始化本区块的previousHash,否则区块之间的联系就断了。
block1 := block.CreateBlock(block.GetHash(&genesisBlock),[]string{"first block"})
block2 := block.CreateBlock(block.GetHash(&block1),[]string{"second block"})
block3 := block.CreateBlock(block.GetHash(&block2),[]string{"third block"})

接下来我们打印出整个区块链的信息看一下:

假如我们修改创世块的信息,变成

genesisBlock := block.CreateBlock("",[]string{"Hello","Block","World"})

那么所有的区块的hash值都会发生很大的变化,这就是为什么在区块上作弊是非常困难的一件事情。

最后,附全部源码

  • src/main/main.go
package main

import (
"block"
"fmt"
) func main(){
genesisBlock := block.CreateBlock("",[]string{"Hello","Block","World"}) block1 := block.CreateBlock(block.GetHash(&genesisBlock),[]string{"first block"})
block2 := block.CreateBlock(block.GetHash(&block1),[]string{"second block"})
block3 := block.CreateBlock(block.GetHash(&block2),[]string{"third block"}) fmt.Println("genesisBlock:",block.GetHash(&genesisBlock))
fmt.Println("block1 :",block.GetHash(&block1))
fmt.Println("block2 :",block.GetHash(&block2))
fmt.Println("block3 :",block.GetHash(&block3))
}
  • src/block/block.go
package block

import (
"block/pb"
"crypto/sha1"
"encoding/hex"
"github.com/gogo/protobuf/proto"
) type Block struct {
previousHash string
transaction []string
} func CreateBlock(preHash string, trans []string) Block{
b := Block{
preHash,trans,
}
return b
} func ToProto(block *Block) proto.Message {
return &blockpb.Block{
PreviousHash: block.previousHash,
Transaction: block.transaction,
}
} func GetHash(block *Block) string{
serialBlock := ToProto(block)
byteBlock, _ := proto.Marshal(serialBlock)
hash := sha1.Sum(byteBlock)
return hex.EncodeToString(hash[:])
}
  • src/block/pb/block.pb.go
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: block.proto package blockpb import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
) // Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf // This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package type Block struct {
PreviousHash string `protobuf:"bytes,1,opt,name=previousHash,proto3" json:"previousHash,omitempty"`
Transaction []string `protobuf:"bytes,2,rep,name=transaction,proto3" json:"transaction,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
} func (m *Block) Reset() { *m = Block{} }
func (m *Block) String() string { return proto.CompactTextString(m) }
func (*Block) ProtoMessage() {}
func (*Block) Descriptor() ([]byte, []int) {
return fileDescriptor_8e550b1f5926e92d, []int{0}
} func (m *Block) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Block.Unmarshal(m, b)
}
func (m *Block) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Block.Marshal(b, m, deterministic)
}
func (m *Block) XXX_Merge(src proto.Message) {
xxx_messageInfo_Block.Merge(m, src)
}
func (m *Block) XXX_Size() int {
return xxx_messageInfo_Block.Size(m)
}
func (m *Block) XXX_DiscardUnknown() {
xxx_messageInfo_Block.DiscardUnknown(m)
} var xxx_messageInfo_Block proto.InternalMessageInfo func (m *Block) GetPreviousHash() string {
if m != nil {
return m.PreviousHash
}
return ""
} func (m *Block) GetTransaction() []string {
if m != nil {
return m.Transaction
}
return nil
} func init() {
proto.RegisterType((*Block)(nil), "blockpb.Block")
} func init() { proto.RegisterFile("block.proto", fileDescriptor_8e550b1f5926e92d) } var fileDescriptor_8e550b1f5926e92d = []byte{
// 104 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4e, 0xca, 0xc9, 0x4f,
0xce, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x07, 0x73, 0x0a, 0x92, 0x94, 0x7c, 0xb9,
0x58, 0x9d, 0x40, 0x4c, 0x21, 0x25, 0x2e, 0x9e, 0x82, 0xa2, 0xd4, 0xb2, 0xcc, 0xfc, 0xd2, 0x62,
0x8f, 0xc4, 0xe2, 0x0c, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x14, 0x31, 0x21, 0x05, 0x2e,
0xee, 0x92, 0xa2, 0xc4, 0xbc, 0xe2, 0xc4, 0xe4, 0x92, 0xcc, 0xfc, 0x3c, 0x09, 0x26, 0x05, 0x66,
0x0d, 0xce, 0x20, 0x64, 0xa1, 0x24, 0x36, 0xb0, 0xf1, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff,
0x68, 0x7e, 0xfd, 0x82, 0x6d, 0x00, 0x00, 0x00,
}
  • src/block/pb/block.proto
syntax="proto3";

package blockpb;

message Block{
string previousHash = 1;
repeated string transaction = 2;
}

从零开始用golang创建一条简单的区块链的更多相关文章

  1. Python创建一个简单的区块链

    区块链(Blockchain)是一种分布式账本(listributed ledger),它是一种仅供增加(append-only),内容不可变(immutable)的有序(ordered)链式数据结构 ...

  2. 300行ABAP代码实现一个最简单的区块链原型

    不知从什么时候起,区块链在网上一下子就火了. 这里Jerry就不班门弄斧了,网上有太多的区块链介绍文章.我的这篇文章没有任何高大上的术语,就是300行ABAP代码,实现一个最简单的区块链原型. 我个人 ...

  3. 用Java实现简单的区块链

    用 Java 实现简单的区块链 1. 概述 本文中,我们将学习区块链技术的基本概念.也将根据概念使用 Java 来实现一个基本的应用程序. 进一步,我们将讨论一些先进的概念以及该技术的实际应用. 2. ...

  4. 使用 java 创建你的第一个区块链(第一部分)

    本系列教程的目的是帮助您了解如何开发区块链技术. 在本教程中,我们将: 创建你的第一个(非常)基本的“区块链”. 实施简单的工作证明(采矿)系统. 惊叹于可能性. (我假设您对面向对象编程有基本的了解 ...

  5. Rust 实现一个简单的区块链

    一.背景 近期用 Rust 实现了 Jeiwan/blockchain_go,与原项目相比没有加入新的功能,只是换了一个编程语言实现了一遍,源码放在 Github 上. 开发这个项目,花费了好几个周末 ...

  6. C# 简单的区块链实现

    1.项目配置 首先新建一个 Asp.Net Core 项目,然后选择 Empty Project(空项目) 类型,建立完成后无需进行任何配置. 2.数据模型 这里我们来创建一个具体的区块数据模型,使用 ...

  7. 使用 java 创建你的第一个区块链(第二部分)

    本系列教程的目的是帮助您了解如何开发区块链技术. 在这第二个教程中,我们将: 创建一个简单的钱包: 使用我们的区块链发送已签名的交易: 感觉更酷. 以上这些将使我们拥有自己的加密货币! 从上一个教程开 ...

  8. Go语言模拟实现简单的区块链

    一.创建项目 按照Go语言最佳实践的思路, 在工作空间下的src目录下创建 github.com/hangzhou-huxin/blokcchain目录作为我们的项目目录,然后用GoLand工具选中这 ...

  9. [Python Study Notes]一个简单的区块链结构(python 2.7)

    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ...

随机推荐

  1. HTML5实时语音通话聊天,MP3压缩传输3KB每秒

    目录 一.把玩方法 二.技术特性 (1)数据传输 (2)音频采集和编码 (3)音频实时接收和播放 三.应用场景 自从Recorder H5 GitHub开源库优化后,对边录边转码成小语音片段文件实时上 ...

  2. MySQL之mysqldump的使用

    一.mysqldump 简介 mysqldump 是 MySQL 自带的逻辑备份工具. 它的备份原理是通过协议连接到 MySQL 数据库,将需要备份的数据查询出来,将查询出的数据转换成对应的inser ...

  3. 浏览 GitHub 太卡了?教你两招!

    老实说,GitHub 在国内的使用体验并不算太好,这其中最大的原因就是网络了. GitHub 访问起来比较卡,这个看起来貌似无解.国内的 gitee 网速倒是可以,但是无法代替 GitHub,个人感觉 ...

  4. nginx-springboot-vue前后端分离跨域配置

    nginx-springboot-vue前后端分离跨域配置 引言 接着上篇--简单的springboot-vue前后端分离登录Session拦截的demo,其中跨域是通过springboot后端全局设 ...

  5. POJ-3261-Milk Patterns-二分+哈希

    Milk Patterns 题意: 在一串数字中,求至少连续k次的最大子序列长度: 思路: 二分加哈希: #include <cstdio> #include <iostream&g ...

  6. 杭电多校第二场 1005 hack it

    题意: 构造一个n*n 的 01 矩阵, 0 < n < 2001,  矩阵需要满足没有一个子矩阵的4个角都是1,并且矩阵内1的个数至少有85000个. 题解:数论构造题 参考From 代 ...

  7. bzoj 4025 二分图 lct

    题目传送门 题解: 首先关于二分图的性质, 就是没有奇环边. 题目其实就是让你判断每个时段之内有没有奇环. 其次 lct 只能维护树,(反正对于我这种菜鸟选手只会维护树), 那么对于一棵树来说, 填上 ...

  8. 最长上升子序列模板 hdu 1087 Super Jumping! Jumping! Jumping!

    Nowadays, a kind of chess game called “Super Jumping! Jumping! Jumping!” is very popular in HDU. May ...

  9. B. Tea Queue codeforces Round.37.div2 队列

    B. Tea Queue time limit per test 1 second memory limit per test 256 megabytes input standard input o ...

  10. Spring Cloud Alibaba | 微服务分布式事务之Seata

    Spring Cloud Alibaba | 微服务分布式事务之Seata 本篇实战所使用Spring有关版本: SpringBoot:2.1.7.RELEASE Spring Cloud:Green ...