区块链(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. 分布式配置中心Apollo——QuickStart

    分布式配置中心 剥离配置文件,实现动态修改,自动更新. [假设没有分布式配置中心,修改配置文件后都需要重启服务,对于数量庞多的微服务开发来说,就会非常繁琐] 分布式配置中心有哪些 disconf(依赖 ...

  2. unity编辑器扩展_04(使用Selection获取选择的游戏物体)

    代码: [MenuItem("Tools/GetChance", false, 1)]    static void GetChance()    {        if (Sel ...

  3. Python基础 2-2 列表的实际应用场景

    引言 本章主要介绍列表在实际应用中的使用场景,多维列表(嵌套列表) 如果你需要在列表保存每个人员的一些基本信息,使用列表嵌套来保存这种信息是个不错的主意. 多维列表 列表可以根据实际情况嵌套使用,比如 ...

  4. Oracle在VMware虚拟机安装的配置

    我是在VMware虚拟机上安装的Oracle , 我只说说我踩过的几个坑吧. VMware的虚拟网络编辑器 仅主机模式相当于在你的主机和虚拟机之间建立了一个局域网,里面只有你的主机和虚拟机 可以通过D ...

  5. Springboot源码分析之事务问题

    摘要: 事务在后端开发中无处不在,是数据一致性的最基本保证.要明白进事务的本质就是进到事务切面的代理方法中,最常见的是同一个类的非事务方法调用一个加了事务注解的方法没进入事务.我们以cglib代理为例 ...

  6. 分布式唯一ID生成算法-雪花算法

    在我们的工作中,数据库某些表的字段会用到唯一的,趋势递增的订单编号,我们将介绍两种方法,一种是传统的采用随机数生成的方式,另外一种是采用当前比较流行的“分布式唯一ID生成算法-雪花算法”来实现. 一. ...

  7. Oralce 触发器

    今天做了一个需要用到触发器实现的功能中间去到了各种问题,还好最后都解决了: 整个过程中真是遇到了不少错误: ORA-04091: 表 KPGO.T_ISSUER 发生了变化, 触发器/函数不能读它 O ...

  8. SCRUM的三个工件

    转自:http://www.scrumcn.com/agile/scrum-knowledge-library/scrum.html#tab-id-6 Scrum 的工件以不同的方式展现工作和价值,可 ...

  9. P1726 上白泽慧音 tarjan 模板

    P1726 上白泽慧音 这是一道用tarjan做的模板,要求找到有向图中最大的联通块. #include <algorithm> #include <iterator> #in ...

  10. POJ 3207 Ikki's Story IV - Panda's Trick 2-sat模板题

    题意: 平面上,一个圆,圆的边上按顺时针放着n个点.现在要连m条边,比如a,b,那么a到b可以从圆的内部连接,也可以从圆的外部连接.给你的信息中,每个点最多只会连接的一条边.问能不能连接这m条边,使这 ...