区块链(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. 常用Http status code 如何记

    一直记不住http常用的status code,最近思考可以这样想.http无非就是客户端和服务端之间请求嘛.结果么要么成功,要么失败. 成功了,可以提示信息 -- Informational 1xx ...

  2. Linux软件包管理(12)

    RPM包管理: RPM(RedHat Package Manager),早期是在RedHat发行版下,由于比较火,所以慢慢运行于各个发行版(如suse,centos等). 它生成具有.RPM扩展名的文 ...

  3. 一键部署 Spring Boot 到远程 Docker 容器,就是这么秀!

    不知道各位小伙伴在生产环境都是怎么部署 Spring Boot 的,打成 jar 直接一键运行?打成 war 扔到 Tomcat 容器中运行?不过据松哥了解,容器化部署应该是目前的主流方案. 不同于传 ...

  4. python入门(六)二次编码与文件操作

    二次编码 密码本: ascii -- 没有中文 英文1字节 gbk -- 英文 8b(位) 1B(字节) 中文 16b 2B unicode -- 英文32b 4B 中文32b 4B utf-8 -- ...

  5. ABC133F - Colorful Tree

    ABC133FColorful Tree 题意 给定一颗边有颜色和权值的树,多次询问,每次询问,首先更改颜色为x的边的权值为y,然后输出u到v的距离. 数据都是1e5量级的. 思路 我自己一开始用树链 ...

  6. 2019DX#6

    Solved Pro.ID Title Ratio(Accepted / Submitted)   1001 Salty Fish 16.28%(7/43)  OK 1002 Nonsense Tim ...

  7. hdu-6601 Keen On Everything But Triangle

    题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=6601 Description N sticks are arranged in a row, and ...

  8. codeforces E. Phone Talks(dp)

    题目链接:http://codeforces.com/contest/158/problem/E 题意:给出一些电话,有打进来的时间和持续的时间,如果人在打电话,那么新打进来的电话入队,如果人没有打电 ...

  9. PythonI/O进阶学习笔记_3.2面向对象编程_python的封装

    前言: 本篇相关内容分为3篇多态.继承.封装,这篇为第三篇 封装. 本篇内容围绕 python基础教程这段: 在面向对象编程中,术语对象大致意味着一系列数据(属性)以及一套访问和操作这些数据的方法.使 ...

  10. Django-下载安装-配置-创建django项目-三板斧简单使用

    目录 Django 简介 使用 django 的注意事项 计算机名不能有中文 Django版本问题 django下载安装 在命令行下载安装 在pycharm图形界面下载安装 检验是否安装成功 创建Dj ...