用go语言撸一个简易版的区块链

用go撸一个简易版的区块链

引言

这个最初的版本时多年以前学习go的时候,自己撸的一个简易版本的区块链。不过麻雀虽小,五脏俱全。通过这个代码你了解区块链内部的大概运行机制时没有问题的。

比特币底层区块链的代码非常复杂,但是我们可以从中梳理几个核心的概念,然后对应进行简单的实现。通过这些简易版本的实现我们可以以小窥大。下面我们先来梳理下几个核心的概念。

交易

拿比特币举例,A给B转账,这是一笔交易。更广义的概念,交易可以形容数据库网络中发生的每一次改变,可以是一笔转账、一个事件通知、或一段信息。交易中通常包含发送者的信息,接收者的信息,交易金额等信息。

这是交易的概念。

区块

区块链 是一个共享的、不可篡改的账本,旨在促进业务网络中的交易记录和资产跟踪流程。拿比特币交易举例,比如A向B转账,这笔交易会在区块链公网进行广播,并在各节点间共享这一信息。每十分钟左右,挖矿者会将这些交易收集到一个新区块中。

这是区块的概念。

把一个个验证后的合法的区块连在一起,形成的就是链。

哈希(hash)

哈希函数(Hash Function),也称为散列函数,给定一个输入x,它会算出相应的输出H(x)。只要输入变化,输出的哈希结果必然也会变化。

区块里面除了包含交易的信息之外,还会将一个加密哈希附加到新区块的最后。如果修改了区块的内容,则该哈希值也将更改,这将提供一种检测数据篡改的方式。

这是哈希的概念。

工作量证明

就是我们俗称的挖矿。矿工(就是区块链节点)创建区块,如果想把区块加入区块链,矿工需要不断的在区块中加入一个随机数并计算一个哈希值,只有这个值小于的某个目标值才能加入链中。这个目标值决定了挖矿的难度。

源码解析

有了上面的知识储备后,我们来分析下源码。

注意我只会贴出来关键的代码,会把不影响核心逻辑的都去掉。 全部的源码再文章最后的链接给出。

整个工程的目录结构时这样的,

在这里插入图片描述

pkg里面存放的是核心业务代码,也就是区块链的核心逻辑。同时也包括对应的单元测试程序。cmd目录下存放的是程序的运行入口,也就是我们的main函数。

我们从入口开始,采用从全局到细节的流程来剖析源码。

func main() {

	port := flag.String("port","8001","use -port <port number>")
	flag.Parse()

	fmt.Printf("port is:%s\n",*port)

	http.HandleFunc("/mine",MineHandler)
	http.HandleFunc("/transactions/new",NewTransactionHandler)
	http.HandleFunc("/nodes/register",RegisterNodesHandler)
	http.HandleFunc("/nodes/resolve",ConsensusHandler)
	http.HandleFunc("/chain",ChainHandler)

	http.ListenAndServe(fmt.Sprintf(":%s",*port),nil)
}

main函数没有业务逻辑,就是通过http的方式给我们提供了测试的入口,让我们可以通过http请求(get或者post)发起对应的功能。

首先我们来看看,注册节点的方法RegisterNodesHandler,所谓的节点就是矿工的意思。我们可以通过命令行的方式(./goblockchain -port 8001)来启动一个节点,同时通过RegisterNodesHandler把其他节点加入进来。

func RegisterNodesHandler(w http.ResponseWriter,req *http.Request) {

	type Nodes_ST struct {
		Nodes []string
	}

	type ResponseJsonBean struct {
		Message string   `json:"message"`
		Data    []string `json:"total_nodes"`
	}

	nodeGroup := Nodes_ST{}
	result := ResponseJsonBean{}


	req.ParseForm()

	b,_ := ioutil.ReadAll(req.Body)

	if req.Method == "POST" {

		json.Unmarshal([]byte(b),&nodeGroup)

		result.Data = make([]string,0)

		for _,node := range nodeGroup.Nodes {

				fmt.Printf("node:%s\n",node)
				goblockchain.Register_Node(node)
				result.Data = append(result.Data,node)
		}

		code = http.StatusCreated
		result.Message = "New nodes have been added"

	}

	bytes,_ := json.Marshal(result)
	w.WriteHeader(code)
	fmt.Fprintf(w,string(bytes))
}

这部分的逻辑就是解析post请求的节点数,然后循环调用(循环次数是节点的数量)调用Register_Node方法注册节点,请求的数据类型下面这样的格式:

{
    "nodes": [
        "http://127.0.0.1:8002","http://127.0.0.1:8003"
    ]
}

继续来看下Register_Node方法,

func (bc *Blockchain) Register_Node(address string) {
	u,err := url.Parse(address)
	bc.nodes = append(bc.nodes,u.Host)
}

其实就是加入了一个blockchain实例的nodes元素的数组里,nodes的定义如下:

type Blockchain struct {
	current_transactions []Transaction
	chain                []block
	nodes                []string
}

然后我们看看在某个节点上创建一笔交易的流程。入口函数是NewTransactionHandler

func NewTransactionHandler(w http.ResponseWriter,req *http.Request) {

    //交易的元数据
	type Transaction_ST struct {
		Sender    string `json:sender`
		Recipient string `json:recipient`
		Amount    int    `json:amount`
	}

	type ResponseJsonBean struct {
		Message string `json:"message"`
	}

	transaction := Transaction_ST{}
	result := ResponseJsonBean{}

	req.ParseForm()
	b,&transaction)

		index := goblockchain.New_Transaction(transaction.Sender,transaction.Recipient,transaction.Amount)

		code = http.StatusCreated
		result.Message = fmt.Sprintf("New nodes have been added to Block %d",index)

	}

	bytes,string(bytes))

}

解析post提交的交易数据,然后请求New_Transaction方法创建交易。请求的数据示例如下:

{
  "sender": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","recipient": "1Ez69SnzzmePmZX3WpEzMKTrcBF2gpNQ55","amount": 1000
}

New_Transaction方法也比较简单,

func (bc *Blockchain) New_Transaction(sender string,recipient string,amount int) int {

	var trans Transaction

	trans.Sender = sender
	trans.Recipient = recipient
	trans.Amount = amount

	bc.current_transactions = append(bc.current_transactions,trans)
	block := bc.Last_block()
	return block.Index + 1

}

创建交易,放到blockchain实例的交易数组里,这里有个Last_block方法也注意下,它的实现如下:

func (bc *Blockchain) Last_block() block {
	height := len(bc.chain)
	block := bc.chain[height-1]
	return block
}

这个方法就是返回了当前区块链的最后一个区块的实例。

然后,就可以挖矿了,哈哈。入口函数是MineHandler

func MineHandler(w http.ResponseWriter,req *http.Request) {

    //区块的数据结构
	type Block_ST struct {
		Index         int           `json:index`
		Message       string        `json:message`
		Transactions  []Transaction `json:transactions`
		Proof         int           `json:proof`
		Previous_hash string        `json:previous_hash`
	}

    
	result := Block_ST{}

	req.ParseForm()


	last_block := goblockchain.Last_block()
	last_proof := last_block.Proof
	previous_hash := last_block.Previous_hash

    //开始挖坑
	proof := goblockchain.Proof_of_work(last_proof)

	var trans_reward Transaction
	trans_reward.Sender = "0"
	trans_reward.Recipient = "random address"
	trans_reward.Amount = 1

    //挖坑成功,形成新的区块并加入链
	block := goblockchain.New_Block(proof,previous_hash)

	code = http.StatusOK

	result.Message = "New Block Forged"
	result.Index = block.Index
	result.Proof = block.Proof
	result.Transactions = block.Transactions
	result.Previous_hash = block.Previous_hash

	bytes,string(bytes))

}

这个代码稍长,不过也很好理解。首先是获取链上最后一个区块的证明值(last_proof),之所以要获取它是因为计算新区块的哈希需要使用上一个区块的证明值。这样能保证整个链上的区块都是互相关联的。

工作量证明的方法是Proof_of_work

func (bc *Blockchain) Proof_of_work(last_proof int) int {
	proof := 0

	for !(valid_proof(last_proof,proof)) {
		proof++
	}

	return proof
}

这里的proof就是新区块一直在尝试的随机值,然后用valid_proof验证是否满足条件(也就是挖矿是否成功),源码如下:

func valid_proof(last_proof int,proof int) bool {

	str_last_proorf := []byte(strconv.FormatInt(int64(last_proof),10))
	str_proof := []byte(strconv.FormatInt(int64(proof),10))

	str_data := bytes.Join([][]byte{str_last_proorf,str_proof},[]byte{})

	guess_hash := sha256.Sum256(str_data)
	return bytes.Equal(guess_hash[:2],[]byte("00"))
}

这里可以看到,计算哈希的时候需要把链上最后一个区块的证明值作为其中一个输入。

挖矿成功后,就可以形成新的区块放入链上了,方法是New_Block,源码如下:

func (bc *Blockchain) New_Block(proof int,previous_hash string) *block {


	blockinstance := &block{}

	blockinstance.Index = len(bc.chain) + 1
	blockinstance.timestamp = time.Now().Unix()
	blockinstance.Transactions = bc.current_transactions
	blockinstance.Proof = proof
	blockinstance.Previous_hash = previous_hash

	trans_len := len(bc.current_transactions)

	fmt.Printf("trans_len:%d \n",trans_len)
	fmt.Printf("index:%d \n",blockinstance.Index)

	bc.current_transactions = bc.current_transactions[trans_len:] //clear
	bc.chain = append(bc.chain,*blockinstance)

	return blockinstance
}

这里的逻辑也很简单,生成一个新的区块实例,加入交易的信息,工作量证明值,上一个区块的哈希,然后链接在区块链的最后即可。

测试

首先编译下源码,进入cmd目录,执行:

$ go build -o goblockchain

然后开启三个命令行窗口,分别执行下面的命令,启动三个节点(矿工)

./goblockchain -port 8001
./goblockchain -port 8002
./goblockchain -port 8003

然后我们在node1执行添加节点的命令,

在这里插入图片描述

可以看到执行成功了。

接着我们创建一笔交易,

在这里插入图片描述

可以看到创建成功了。

然后执行挖矿

在这里插入图片描述

可以看到挖矿成功了。

然后我们可以查看下当前区块链的信息:

curl 127.0.0.1:8001/chain

返回的结果是:

{Index:1 timestamp:1655738834 Transactions:[] Proof:1 Previous_hash:1} --> {Index:2 timestamp:1655738903 Transactions:[{Sender:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa Recipient:1Ez69SnzzmePmZX3WpEzMKTrcBF2gpNQ55 Amount:1000}] Proof:9561 Previous_hash:1} --> 

可以看到是有两个区块,第一个是默认生成的不包含交易信息,第二个区块就是我们自己挖的。


源码地址如下:

https://github.com/pony-maggie/blockchain

原文地址:https://blog.csdn.net/pony_maggie

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


文章浏览阅读903次。文章主要介绍了收益聚合器Beefy协议在币安智能链测试网网上的编译测试部署流程,以Pancake上的USDC-BUSD最新Curve版流动池的农场质押为例,详细介绍了完整的操作流程。_怎么在bsc网络上部署应用
文章浏览阅读952次。比特币的主要思路是,构建一个无中心、去信任的分布式记账系统。交易签名只能保证交易不是他人伪造的,却不能阻止交易的发起者自己进行多重交易,即交易的发起者将一个比特币同时转账给两个人,也就是所谓的双花。比特币应用的区块链场景也叫做公链,因为这个区块链对所有人都是公开的。除此之外,还有一种区块链应用场景,被称作联盟链。区块链的出现,使得低成本,去信任的跨组织合作成为可能,将重构组织间的关系,这个关系既包括企业间的关系,也包括政府和企业间的关系,还有政府部门间的关系。
文章浏览阅读2.5k次。虚拟人从最初的不温不火,到现在步入“出生高峰期”,元宇宙可以说是功不可没。此前,量子位发布了《虚拟数字人深度产业报告》,报告显示,到2030年我国虚拟数字人整体市场规模将达到2700亿元。其中,“身份型虚拟人”市场规模预计达到1750亿元,占主导地位,而“服务型虚拟人”总规模也将超过950亿元。得益于AI、VR/AR 等技术的发展,虚拟人的应用场景正在从传统的虚拟偶像等娱乐行业迈向更多元化的领域。_最喜欢的虚拟角色
文章浏览阅读1.3k次,点赞25次,收藏13次。通过调查和分析用户需求、兴趣和行为,你可以更好地定位你的目标受众,并在市场中找到你的定位。在设计你的Web3.0项目时,注重用户界面的友好性、交互流畅性和功能的创新性,以提供独特的用户体验。通过与有影响力的人或组织进行合作,推广你的Web3.0项目。通过与他们分享你的项目并抓住他们的推荐,可以迅速获得更多的关注度。通过优化你的网站和内容,将有助于提高你的排名,并增加有机流量。通过提供奖励激励计划,如空投、奖励机制等,激励用户参与你的Web3.0项目。的人或组织合作,可以增加你的项目的曝光度。
文章浏览阅读1.7k次。这个智能合约安全系列提供了一个广泛的列表,列出了在 Solidity 智能合约中容易反复出现的问题和漏洞。Solidity 中的安全问题可以归结为智能合约的行为方式不符合它们的意图。我们不可能对所有可能出错的事情做一个全面的列表。然而,正如传统的软件工程有常见的漏洞主题,如 SQL 注入、缓冲区超限和跨网站脚本,智能合约中也有反复出现的。_solidity安全漏洞
文章浏览阅读1.3k次。本文描述了比特币核心的编译与交互方法_编译比特币
文章浏览阅读884次。四水归堂,是中国建筑艺术中的一种独特形式。这种形式下,由四面房屋围出一个天井,房屋内侧坡向天井内倾斜,下雨时雨水会从东西南北四方流入天井,从而起到收集水源,防涝护屋的作用,寓意水聚天心,天人合一。在科技产业当中,很多时候我们需要学习古人的智慧与意蕴,尝试打通各个生态,聚四方之力为我所用,这样才能为最终用户带来最大化价值。随着数字化、智能化的发展,算力成为生产力的根基。在这一大背景下,算力需要贯通软..._超聚变csdn
文章浏览阅读1k次,点赞24次,收藏19次。云计算和区块链是当代科技领域两个备受关注的核心技术。本文将深入探讨云计算和区块链的发展历程,详细剖析其起初阶段的奠基、面临的问题、业务内容、当前研究方向、用到的技术、实际应用场景、未来发展趋势,并提供相关链接供读者深入了解。
文章浏览阅读1.5k次。融入对等网络的奥妙,了解集中式、全分布式和混合式对等网络的差异,以及区块链网络的结构与协议,让你跃入区块链的连结网络。揭开密码学的神秘面纱,探寻对称密码学、非对称密码学、哈希函数、数字签名等关键技术,让你了解信息安全的核心。解码共识算法的精髓,从理论到实践,从PoW、PoS到PBFT,让你深入了解区块链如何达成共识。探索智能合约的世界,从定义到生命周期,从执行引擎到开发与部署,带你进入无限可能的合约领域。了解令人惊叹的区块链世界,从概念到价值,从发展历程到政策法规,一篇章串联出区块链的精髓。
文章浏览阅读777次。8 月份,加密货币市场经历了明显的波动,比特币价格波动幅度较大。与此同时,NFT 市场出现大幅下跌,引发了人们对这一新兴行业未来发展趋势的担忧
文章浏览阅读8.8k次,点赞53次,收藏37次。近二十年来,我国信息科技发展日益成熟,出现的网络完全问题也是“百花齐放”。而元宇宙作为5G技术、AR/VR技术、云计算以及区块链等技术的组合体,其安全性指定会被人们所广泛关注。根据前面所讲,元宇宙融合了虚拟世界和现实世界,通过数据将现实世界的各种元素映射到数字化的虚拟世界中。所以没有数据,就等于没有元宇宙的一切;没有信息安全,元宇宙的社会生产、生活就不能正常有序地进行。所以足以可见数据安全、信息安全对元宇宙发展起到的重要作用!!_元宇宙 安全计算
文章浏览阅读1.4k次。最早使用历史 1991年采用 时间戳 追溯 数字文档,之后 2009年后创始人**中本聪** (satoshi nakamoto )日裔美国人,在设计比特币数字货币中将此理念写入应用程序中_web3.0学习
文章浏览阅读1.7k次。DeFi收益来源全面概述_drfi收益
文章浏览阅读941次,点赞17次,收藏21次。号外:教链内参1.28《从BTC现货ETF的近期数据看到的》隔夜BTC经历现货ETF通过后的情绪冷静,一度破位40k后又逐渐修复至42k上方。请珍惜42k的BTC吧。也许到下个周期,我们将不再有机会见到这个高度的BTC了。下面,让我们重温,42k的BTC,在过去四年穿越牛熊的过程中,带给我们的启迪吧。需要提醒的是,历史文字,自有历史局限性,回顾,也须带着批判性的目光阅读和审视。2021年2月8日,...
文章浏览阅读1.2k次,点赞23次,收藏21次。其实一开始我也是这么想的,但根据PoW算法机制,如果你的计算量不够大,是无法控制区块链的走向的,也就是说,即使你投入了大量的成本用于完成任务,也不能保证自己成功。例如,你持有100个币,总共持有了30天,那么,此时你的币龄就为3000,这个时候,如果你发现了一个PoS区块,那么你的币龄就会被减去一定的值,每减少365个币龄,将会从区块中获得0.05个币的利息(可理解为年利率5%),那么在这个案例中,利息=3000×5%/365=0.41个币。前面说过,谁的算力强,谁最先解决问题的概率就越大。
文章浏览阅读1.9k次。这里主要实现的部分继续下去,对 Blockchain 这个对象有一些修改,如果使用 TS 的话可能要修改对应的 interface,但是如果是 JS 的话就无所谓了。需要安装的依赖有:express现在的 express 已经不内置 body-parser,需要作为单独的依赖下载request不下载会报错,是使用 request-promise 所需要的依赖和已经 deprecated 了,具体 reference 可以参考。_js区块链
文章浏览阅读1k次,点赞19次,收藏19次。作者:Zach Pandl Grayscale编译:象牙山首席村民 碳链价值以太坊在2023年取得了丰厚的回报。但表现不如比特币以及其他一些智能合约公链代币。我们认为,这反映了今年比特币特有的积极因素以及以太坊链上活动的缓慢复苏。尽管以太坊的涨幅低于比特币,但从绝对值和风险调整值来看,今年以太坊的表现优于传统资产类别。以太坊不断增长的L2生态系统的发展可能会吸引新用户,并在2024年支撑以太币的...
文章浏览阅读908次,点赞20次,收藏20次。通证是以数字形式存在,代表的是一种权利、一种固有和内在的价值。徐教授告诉我:多年的职业经历,多年的为易货贸易的思考,认识到在处理贸易和经济领域的关系时,应以提高人民生活水平、保证社会成员充分就业、保证就业成员实际收入和有效需求的大幅稳定增长、实现世界资源的充分利用以及扩大货物的生产和交换为目的,期望通过达成互惠互利安排,实行公开、公平、公正的“三公原则”,开展国家与国家、企业与企业之间的易货贸易,规避因信用问题引起的各类风险,消除国际贸易中的歧视待遇,促进全球国家的经济发展,从而为实现上述目标做出贡献。
文章浏览阅读2.5k次。由于webase文档原因,查找起来比较局限,有时候想找一个api却又忘了在哪个模块的目录下,需要一步一步单独点,而利用文档自带的检索功能又因为查找文档全部信息,显得十分缓慢,所以整理了有关WeBASE的api列表但不可否认,现在只有列表,没有对应的页面跳转,文章目的也只是为了多了解webase的接口_webase私钥管理里获取
文章浏览阅读1.4k次,点赞28次,收藏21次。基于​openzeppelin来构建我们的NFT,并用一个例子来手把手的说明如何在opensea快速发布自己的NFT智能合约(ERC721)。