Go学习之go-ethereum【以太坊】源码分析(一)

关于Go语言环境的安装与配置,我在《入门篇》进行了详细讲解,有需要的朋友可以前往阅读,本文进入当下比较火热的区块链项目 - 以太坊(go-ethereum)进行源码解读。本文内容纯属个人见解,有错误理解或者不足之处还请见谅,欢迎一起交流学习。

    - 环境准备

    - 以太坊初识

    - go-ethereum 源码目录

    - 黄皮书、白皮书

    - 以太坊定义

    - 源码中的rlp(部分)


1、环境准备:

    - Git安装

    - Go安装与配置(“GOROOT、GOPATH等等”)



    - Go开发者工具安装(本主使用的是Goland)

    - Win10 64位环境系统(系统不限,适用即可)

//本主配置的GOPATH路径为, Windows环境开发
C:\Go\workspace

//使用命令进行下载源码:
go get github.com/ethereum/go-ethereum

    下载之后使用GoLand开发工具打开源码。进行测试:


由于以太坊的源码较多,可能下载之后会有相关的依赖没有全部下载,如果出现报错信息,根据报错信息找到相应的行号,同样使用go get命令下载即可。


2、以太坊的初步认识

相信可能还有部分朋友对以太坊是第一次接触,在这里就简单地介绍一下。什么是以太坊(来自百度百科)



随着“区块链”这一名词的狂欢式普及,国内众多巨头公司纷纷张开双臂拥抱,把区块链当做互联网时代的伟大颠覆性创新,一窝蜂研究怎样把区块链变成自己抢占商业先机的工具。从本质上是解决信任问题、降低信任成本的技术方案,实现去中心化,去信用中介。

当然,目前就数字货币的市场交易和国内的政策正处于探索阶段的原因,在17年9月,也发布了《关于防范代币发行融资风险公告》,以此要求各类金融行业组织应当做好政策解读,督促会员单位自觉抵制与代币发行融资交易及“虚拟货币”相关的非法金融活动,远离市场乱象,加强投资者教育,共同维护正常的金融秩序。

但同时,藏在数字货币背后的区块链技术被看好,在政策上,逐渐在越来越多的国家当中得到支持。如需要更加深入的了解区块链知识,我会在后续的发文中进行详细介绍。在这不做展开(跑题了,orz)。


3、go-ethereum 目录

accounts以太坊账户管理
bmt二进制的默克尔树
build编译和构建的一些脚本和配置
cmd命令行工具
common公共的工具类
compression实现以太坊数据运行时的一些编码
consensus共识算法
core核心数据结构和算法
cryptohash算法、加密算法
ethETH协议
ethclientRPC客户端
ethdbeth数据库
ethstats提供网络状态的报告
event处理实时事件
les以太坊的轻量级协议子集
light以太坊轻量级客户端提供按需检索的功能
log日志信息
metrics提供磁盘计数器
miner以太坊的区块创建和挖矿
mobile移动端
node以太坊的多种类型的节点
p2p以太坊p2p网络协议
rlp以太坊序列化处理
rpc远程方法调用
swarm网络处理
tests测试
trie以太坊重要的数据结构
whisper提供了whisper节点的协议
其它console、contracts等


4、以太坊黄皮书介绍

有兴趣阅读以太坊黄皮书具体内容的可以参考《以太坊黄皮书连载》中文版《崔广斌的黄皮书》中文版、wiki的《白皮书》英文版去详细了解。其中讲解了复杂的数学问题和看起来很阔怕的公式(orz,反正我是给跪了)。希望你们看完之后能有一些更深入的了解。

以太坊黄皮书是关于以太坊技术的实现规范。

《白皮书》


《黄皮书》


简介

随着互联网连接了世界上绝大多数地方,全球信息共享 的成本越来越低。比特币网络通过共识机制、自愿遵守的社 会合约, 实现一个去中心化的价值转移系统且可以在全球范 围内自由使用, 这样的技术改革展示了它的巨大力量。这样 的系统可以说是加密安全、基于交易的状态机的一种具体 应用。后续类似这样的系统, 如域名币(Namecoin), 从最 原先的货币应用发展到了其它应用, 虽然它只是其中很简单 的一种应用。

以太坊是一个尝试达到通用性的技术项目, 可以构建任 何基于交易的状态机。而且以太坊致力于为开发者提供一 个紧凑的、整合的端到端系统, 这个系统提供了一种可信的消息传递计算框架让开发者以一种前所未有的范式来构建软件。


一些基本规则

1、对于大多数的函数来说,都用大写字母来标识。
2、元组一般用大写字母来标识。
3、标量或者固定大小的字节数组都用小写字母标识。 比如 n 代表交易的nonce, 有一些可能有例外,比如δ代表一个给定指令需要的堆栈数据的多少。

4、变长的字节数组一般用加粗的小写字母。 比如 o 代表一个message call的输出数据。对于某些重要的也可能使用加粗的大写字母。


5、以太坊的定义

以太坊在整体上可以看作一个基于交易的状态机:起始于一个创世块(Genesis)状态,然后随着交易的执行状态 逐步改变一直到最终状态, 这个最终状态是以太坊世界的权 威版本。状态中包含的信息有: 账户余额、名誉度、信誉度、 现实世界的附属数据等; 简而言之,能包含电脑可以描绘 的任何信息。因此,交易是连接两个状态的有效桥梁;“有效”非常重要—因为无效的状态改变远超过有效的状态改变。

简单来说,就是通过读取一系列的输入,然后根据这些输入,会转换成一个新的状态出来。



6、源码中的rlp

RLP是Recursive Length Prefix的简写。是以太坊中的序列化方法,以太坊的所有对象都会使用RLP方法序列化为字节数组。这里我希望先从黄皮书来形式化上了解RLP方法, 然后通过代码来分析实际的实现。

目录

decode.go解码器,把RLP数据解码为go的数据结构
decode_tail_test.go解码器测试代码
decode_test.go解码器测试代码
doc.go文档代码
encode.go编码器,把GO的数据结构序列化为字节数组
encode_test.go编码器测试代码
encode_example_test.go编码器测试代码
raw.go未解码的RLP数据
raw_test.goRLP测试代码
typecache.go类型缓存, 类型缓存记录了类型->(编码器|解码器)的内容

rlp的组合类别

    - 字节数组

    - 数据结构(类List)

var (
	typeCacheMutex sync.RWMutex                  //读写锁,用来在多线程的时候保护typeCache这个Map
	typeCache      = make(map[typekey]*typeinfo) //核心数据结构,保存了类型->编解码器函数
)
type typeinfo struct { //存储了编码器和解码器函数
	decoder
	writer
}
type typekey struct {
	reflect.Type
	// the key must include the struct tags because they
	// might generate a different decoder.
	tags
}

其中的typeCache便是核心,value中存储着对应的编码和解码器

获取编码器和解码器:

func cachedTypeInfo(typ reflect.Type, tags tags) (*typeinfo, error) {
	typeCacheMutex.RLock()		//加读锁来保护,
	info := typeCache[typekey{typ, tags}]
	typeCacheMutex.RUnlock()
	if info != nil { //如果成功获取到信息,那么就返回
		return info, nil
	}
	// not in the cache, need to generate info for this type.
	typeCacheMutex.Lock()  //否则加写锁 调用cachedTypeInfo1函数创建并返回, 这里需要注意的是在多线程环境下有可能多个线程同时调用到这个地方,所以当你进入cachedTypeInfo1方法的时候需要判断一下是否已经被别的线程先创建成功了。
	defer typeCacheMutex.Unlock()
	return cachedTypeInfo1(typ, tags)
}

func cachedTypeInfo1(typ reflect.Type, tags tags) (*typeinfo, error) {
	key := typekey{typ, tags}
	info := typeCache[key]
	if info != nil {
		// 其他的线程可能已经创建成功了, 那么我们直接获取到信息然后返回
		return info, nil
	}
	// put a dummmy value into the cache before generating.
	// if the generator tries to lookup itself, it will get
	// the dummy value and won't call itself recursively.
	//这个地方首先创建了一个值来填充这个类型的位置,避免遇到一些递归定义的数据类型形成死循环 
	typeCache[key] = new(typeinfo)
	info, err := genTypeInfo(typ, tags)
	if err != nil {
		// remove the dummy value if the generator fails
		delete(typeCache, key)
		return nil, err
	}
	*typeCache[key] = *info
	return typeCache[key], err
}

生成对应类型的编解码器函数:

func genTypeInfo(typ reflect.Type, tags tags) (info *typeinfo, err error) {
	info = new(typeinfo)
	if info.decoder, err = makeDecoder(typ, tags); err != nil {
		return nil, err
	}
	if info.writer, err = makeWriter(typ, tags); err != nil {
		return nil, err
	}
	return info, nil
}



----------------------------------------------------------------------

未完待续,敬请期待......

另外,文章部分内容和图片来自ZtesoftCS的github、春花秋梦的以太坊原理,在此鸣谢。

有任何建议或问题,欢迎加微信一起学习交流,欢迎从事IT,热爱IT,喜欢深挖源代码的行业大牛加入,一起探讨。

个人微信号:bboyHan


展开阅读全文

没有更多推荐了,返回首页