【区块链Go语言实现】Part 1:区块链基本原型

 

0x00 介绍

区块链(Blockchain)是21世纪最具革命性的技术之一,目前它仍处于逐渐成熟阶段,且其发展潜力尚未被完全意识到。从本质上讲,区块链只是一种记录的分布式数据库。但它之所以独特,是因为它并不是一个私有的数据库,而是一个公共数据库,也就是说,每个使用它的人都有一份完整或部分的数据副本。并且,只有在数据库的其他持有者同意的情况下,才可以向区块链中添加新的记录。此外,正是区块链使得加密货币和智能合约成为可能。

在本系列文章中,我们将基于区块链构建一种简单的加密货币。

0x01 区块

首先,我们从“区块链”中的“区块”介绍开始。在区块链中,区块是存储有价值信息的块。例如,比特币区块用于存储交易,这是任何一种加密货币的本质。除此之外,区块还包含一些技术信息,比如它的版本、当前时间戳和前一区块的散列值(哈希值)。

在本文中,我们不打算实现区块链或比特币规范中描述的那种区块,而是使用它的简化版本,即我们将要实现的区块结构中只包含重要的信息。下面代码中为我们的区块结构:

type Block struct {Timestamp     int64Data          []bytePrevBlockHash []byteHash          []byte
}

Timestamp(时间戳)是区块创建时刻的时间戳,Data是包含在区块中的实际有价值的信息,PrevBlockHas存储前一区块的散列值,而Hash是当前区块的散列值。在比特币规范中,Timestamp、PrevBlockHash和Hash是区块头,它们组成了一个单独的数据结构,而交易(在我们的例子中是Data)也是一个单独的数据结构。为了简单起见,我们此处将它们混合在一起。

那么如何计算散列值呢?散列值的计算方式是区块链的一个十分重要的特性,正是该特性使得区块链变得安全。计算一个区块的散列值是一种计算上十分困难的操作,即使在快速计算机上也需要一些时间(这就是人们会购买强大的GPU来挖比特币的原因)。这是一种有意的架构设计,它使得添加新的区块变得很困难,因此可以防止对已有区块的篡改。我们将在以后的文章中讨论并实现这种机制。

现在,我们仅仅使用区块字段,连接它们,并在连接的组合上计算一个SHA-256散列值。下面,我们使用SetHash方法来实现这一操作:

func (b *Block) SetHash() {timestamp := []byte(strconv.FormatInt(b.Timestamp, 10))headers := bytes.Join([][]byte{b.PrevBlockHash, b.Data, timestamp}, []byte{})hash := sha256.Sum256(headers)b.Hash = hash[:]
}

接下来,依照Golang惯例,我们将实现一个函数来简化区块的创建:

func NewBlock(data string, prevBlockHash []byte) *Block {block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}}block.SetHash()return block
}

这样,我们就准备好了区块的工作!

0x02 区块链

现在,我们来实现一个区块链。实质上,区块链只是一个包含某些特定结构的数据库:它是一种有序的反向链接列表。这意味着区块以插入顺序进行存储,并每个区块会链接到前一区块。这个结构能够保证快速获得一个区块链中最新的区块,并能够通过区块散列值高效地获取到该区块。

在Golang中,可以使用一个数组和一个map来实现这种结构:数组用于保存有序的散列(在Go中数组是有序的),而map用于保存散列->区块对(map是无序的)。然而,对于我们的区块链原型来说,我们仅仅使用一个数组,因为现在我们不需要通过区块的散列来获得对应的区块。

type Blockchain struct {blocks []*Block
}

这是我们的第一个区块链,我从未想过它会这么容易。

现在我们实现添加区块的功能:

func (bc *Blockchain) AddBlock(data string) {prevBlock := bc.blocks[len(bc.blocks)-1]newBlock := NewBlock(data, prevBlock.Hash)bc.blocks = append(bc.blocks, newBlock)
}

这样就完了么?No!

为了向区块链中添加一个新区块,我们需要确保已经存在了一个区块,但是目前我们的区块链中并未存在任何区块。所以,在任何区块链中,都必须存在至少一个区块,区块链中的首个区块称为创世区块。下面,我们实现一个方法来创建创世区块:

func NewGenesisBlock() *Block {return NewBlock("Genesis Block", []byte{})
}

现在,我们可以实现一个函数来使用创世区块创建一个区块链:

func NewBlockchain() *Blockchain {return &Blockchain{[]*Block{NewGenesisBlock()}}
}

下面,我们来检查一下我们的区块链是否能够正常工作:

func main() {bc := NewBlockchain()bc.AddBlock("Send 1 BTC to Ivan")bc.AddBlock("Send 2 more BTC to Ivan")for _, block := range bc.blocks {fmt.Printf("Prev. hash: %x\n", block.PrevBlockHash)fmt.Printf("Data: %s\n", block.Data)fmt.Printf("Hash: %x\n", block.Hash)fmt.Println()}
}

输出结果如下:

Prev. hash:
Data: Genesis Block
Hash: aff955a50dc6cd2abfe81b8849eab15f99ed1dc333d38487024223b5fe0f1168Prev. hash: aff955a50dc6cd2abfe81b8849eab15f99ed1dc333d38487024223b5fe0f1168
Data: Send 1 BTC to Ivan
Hash: d75ce22a840abb9b4e8fc3b60767c4ba3f46a0432d3ea15b71aef9fde6a314e1Prev. hash: d75ce22a840abb9b4e8fc3b60767c4ba3f46a0432d3ea15b71aef9fde6a314e1
Data: Send 2 more BTC to Ivan
Hash: 561237522bb7fcfbccbc6fe0e98bbbde7427ffe01c6fb223f7562288ca2295d1

通过上面的结果,我们可以确定该区块链可以正常工作。

0x03 结论

本文中,我们创建了一个非常简单的区块链原型:它仅仅是一个区块数组,其中每个区块都有一个链接到前一区块,不过实际的区块链要比这复杂得多。在我们的区块链中,添加新的区块简单快速,但在实际的区块链中添加新区块则需要做一些工作:在获取添加区块的权限之前,区块添加者(或者说节点)必须执行一些繁重的计算(这种机制称为工作证明,Proof-of-Work,POW)。此外,区块链是一种分布式数据库,所以并没有一个唯一的决策者。因此,一个新的区块必须由网络中的其他参与者确认和批准(这种机制称为共识机制)。最后,目前我们的区块链中还没有交易!在后续的文章中我们将逐步讨论这些功能和特点。

下一篇《【区块链Go语言实现】Part 2:工作量证明机制POW》中我们将介绍区块链中的工作量证明机制。

 

英文链接:https://jeiwan.cc/posts/building-blockchain-in-go-part-1/

转载于:https://www.cnblogs.com/leejack/p/8259723.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/369899.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java_基础—FileOutputStream

package com.soar.stream;import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException;public class Demo2_FileOutputStream {/** FileOutputStream 在创建对象的时候,如果没有这个文件会帮我们创建出来* 如果有这个文件…

小熊错误_新手爸妈第一年带娃时,很容易犯的7个错误,对照看看你中了没

文 /关注小熊育儿,帮您轻松育儿带娃不是一件容易的事,对爸妈的时间和体力都是一种考验,不仅“熊孩子”让人头疼,育儿知识差,不懂科学育儿理念的“熊爸妈”也是让人很无奈的,最终受伤的还是孩子,…

轻量级Web应用程序框架:PrimeFaces(JSF)+ Guice + MyBatis(第2部分)

在这一部分中,我将继续演示JSF,Guice和MyBatis的集成。 在持久层中使用DBCP连接池和MYSQL数据库。 看一下第1部分 。 在上一篇文章中 ,我们创建了一个ServletContextListener。 现在,我们只需要在contextInitialized方法中绑定Ba…

python--装饰器、生成器、迭代器、元类

一、装饰器 def w1(func):def inner():func()return innerw1 def f1():print(1111111) 1.只要python解释器执行到装饰器,就会自动进行装饰,而不是等到调用的时候才进行装饰。 2.有参数的函数,装饰器定义方法 def w1(func):def inner(*args, *…

scss的使用方式(环境搭建)

我用的是Koala。 IDE是intellij_idea(其他IDE也可) 下载Koala:http://koala-app.com/ 2.安装(选好位置,下一步即可) 3.打开Koala,创建项目 》创建css文件夹》创建XXX.scss文件,拖到…

小程序 按需_小程序想要留住用户需哪些举措?

**当下许多企业在进行长沙小程序开发后,都会费劲心思在推广引流上,很多企业也确实起到了不错的效果,用户们也是纷纷来到企业的小程序中。只不过有的小程序却无法将用户留下来,导致用户流失,令推广付之东流。下面长沙小…

渍渍渍~来一发豆瓣自动登录玩玩~

今天来一个比较屌的自动化登陆工具,selenium模块,这个模块,比较装逼啦~具体自行百度吧!这篇博客实现了什么功能呢~可以自动使用用户名和密码登录,当然,豆瓣对登录次数还有ip地址做了一些策略,例…

Redis数据库入门学习(下载与安装、常用命令、在Java中操作Redis)

简介 下载与安装 数据类型 常用命令 1.字符串操作命令 2.哈希操作命令 3.列表操作命令 push是将元素总是插入到第一个 0表示第一个,1表示第二个。-1表示倒数第一个,-2表示倒数第二个。当前命令的意思是第一个到倒数第一个,即就是全部元素 rpo…

Java EE 7公共草案已发布。 我需要Java EE Light Profile!

2012年12月20日,Java EE 7的公共草案已上载。 乍一看,新规范是对Java EE 6中后续规范的改进。例如,我真的很喜欢Web Profile的想法。 遗憾的是它不是Java EE 6 Web Profile的一部分。 Web Profile是针对现代Web应用程序IMO的开发人员的&…

css入门基础知识

属性的值大于一个单词&#xff0c;要加上引号。 外部样式表<link href "mycss.css" type"text/css" rel "stylesheet">href是css文件&#xff0c;类型是text/css,rel是当前文档与外部文档的关系。 内部样式表<style>body{color:re…

.NET使用Office Open XML导出大量数据到 Excel

我相信很多人在做项目的都碰到过Excel数据导出的需求&#xff0c;我从最开始使用最原始的HTML拼接&#xff08;将需要导出的数据拼接成TABLE标签&#xff09;到后来happy的使用开源的NPOI, EPPlus等开源组件导出EXCEL&#xff0c;但不久前&#xff0c;我在一个项目碰到一个需求…

netapp管理地址_NetApp常用管理命令总结

一、系统相关&#xff1a;sysconfig -v查看硬件信息,主要显示了硬件平台、版本、序列号、适配器(网卡、光纤卡等)、硬盘等硬件信息sysconfig -r显示磁盘、raid、volume信息&#xff0c;等同于vol status -r-每个RAID会显示RAID的类型、RAID中每个磁盘是属于数据盘(data)、校验盘…

Java 8中的默认方法(Defender方法)简介

我们都知道Java中的接口仅包含方法声明&#xff0c;而没有实现&#xff0c;并且任何实现该接口的非抽象类都必须提供实现。 让我们看一个例子&#xff1a; public interface SimpleInterface {public void doSomeWork(); }class SimpleInterfaceImpl implements SimpleInterfa…

CSS自定义消息提示

1.效果 2.源码 <% page contentType"text/html;charsetUTF-8" language"java" %> <html> <head><style type"text/css">#confirm{position: absolute;z-index: 1;display: inline-block;border: 1px solid black;backgr…

转 push本地代码到github出错

https://www.douban.com/note/332510501/ 刚创建的github版本库&#xff0c;在push代码时出错&#xff1a;$ git push -u origin masterTo gitgithub.com:******/Demo.git! [rejected] master -> master (non-fast-forward)error: failed to push some refs to gitgithub.co…

go语音实战读后感——一

1、第一个go程序&#xff1a; package mainimport ("fmt" )func main() {fmt.Println("Hello go") } 解析&#xff1a;package引进go程序组织包&#xff0c;通过import导入外部代码&#xff0c;标准库中的fmt包用于格式化并输出;通过main主函数进行执行程序…

file 选择的文件胖多有多大_如何删除 macOS 压缩包中的隐藏文件?

如何删除 macOS 压缩包中的隐藏文件&#xff1f;在工作中&#xff0c;压缩打包文件是不可缺少的一项工作。为了避免文件的损坏和缺失&#xff0c;我们通常在macOS系统中&#xff0c;把多个文件或文件夹压缩后进行传输&#xff0c;我们一般使用访达&#xff08;Finder&#xff0…

使用Camel在来自不同来源的Solr中索引数据

Apache Solr是建立在Lucene之上的“流行的&#xff0c;快速的开源企业搜索平台”。 为了进行搜索&#xff08;并查找结果&#xff09;&#xff0c;通常需要从不同的来源&#xff08;例如内容管理系统&#xff0c;关系数据库&#xff0c;旧系统&#xff09;中提取数据&#xff0…

CSS画各种二维图形

1.效果 2.源码 <% page contentType"text/html;charsetUTF-8" language"java" %> <html> <head><style type"text/css">#triangle_top{display: inline-block;width:0;height:0;border-left:50px solid transparent;bor…

成都网站建设企业网站备案需要哪些资料?

一、企业备案所需资料&#xff1a;①主办单位有效证件扫描件&#xff08;营业执照&#xff09;&#xff1b;②主办单位负责人有效证件复印件&#xff08;身份证&#xff09;&#xff1b;③主办单位负责人法人授权书扫描件&#xff08;主体负责人是法人&#xff0c;这个就不用上…