区块链(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. POJ 1661 暴力dp

    题意略. 思路: 很有意思的一个题,我采用的是主动更新未知点的方式,也即刷表法来dp. 我们可以把整个路径划分成横向移动和纵向移动,题目一开始就给出了Jimmy的高度,这就是纵向移动的距离. 我们dp ...

  2. IO核心子系统

    IO核心子系统 一.IO层次结构 IO实现普遍采用了层次式的结构.其基本思想与计算机网络中的层次结构相同:将系统IO的功能组织成一系列的层次,每一层完成整个系统功能的一个子集,其实现依赖于下层完成更原 ...

  3. coco标注信息与labelme标注信息的详解、相互转换及可视化

    引言 在做实例分割或语义分割的时候,我们通常要用labelme进行标注,labelme标注的json文件与coco数据集已经标注好的json文件的格式和内容有差异.如果要用coco数据集的信息,就要对 ...

  4. JS高级(摘自简书)

    JS高级 1. 访问对象属性(方法也是属性)的通用方式:obj['属性名'] 1. 属性名包含特殊字符,如"-".空格,访问:obj['content-type'] 2. 属性名不 ...

  5. yzoj P1126 塔 题解

    题意:给n个积木,搭成两个高度相同的塔,问最高高度 正解是dp 答案在dp[n][0] 代码 #include<bits/stdc++.h> using namespace std; in ...

  6. 【Redis】集群方式

    一.概述 1.1 Redis3.0版本之前 1.2 常见集群方案 二.Redis-Cluster原理 三.搭建集群方案 3.1 准备工作 3.2 创建模拟集群的文件夹 3.3 复制脚本 3.4 复制一 ...

  7. 【Ehcache】基础知识学习

    一.Ehcache概述 1.1 简介 1.2 Ehcache的主要特性 二.Ehcache使用介绍 2.1 Ehcache缓存过期策略 2.2 如何解决缓存与db不同步的问题. 三.Ehcache 基 ...

  8. Intellij IDEA在maven项目中添加外部Jar包运行

    一. 问题概述 我们知道Intellij IDEA是非常好用的Java语言开发的集成环境.提供了非常多实用的功能,包括了智能代码助手.代码自动提示.代码重构.各种插件等,当然也集成了maven 正常情 ...

  9. 【入门】广电行业DNS、DHCP解决方案详解(三)——DNS部署架构及案

    [入门]广电行业DNS.DHCP解决方案详解(三)——DNS部署架构及案 DNS系统部署架构 宽带业务DNS架构 互动业务DNS架构 案例介绍 案例一 案例二 本篇我们将先介绍DNS系统部署架构体系, ...

  10. JavaScript漫谈之理解类型操作符typeof

    在本文中,将简述JavaScript类型系统和数据类型,以及如何使用typeof操作符执行类型检查. 还讲解了使用typeof操作符进行某些数据类型检查是不完善的,并介绍其他几种类型检查的方法. 更多 ...