以太坊索引
table.go 定期随机选取一些节点找他们要他们的节点,放到本地,也就是一个随机找节点的table 里头的bucket 和 nodesByDistance都是为了找节点方便(这里已经有pingpong了)使用的是udp.go
p2p/dial.go目前的理解是table负责用udp发现节点,而dial.go负责发起真正的链接,调用dial和server.go的SetupConn
udp.go 主要是收发消息用的比如ping pong 找节点消息等等,点对点消息
discover/node.go 代表了一个网路节点,其中nodeid是一个公钥的转换
p2p/peer.go 代表一个节点,主要是pingpong消息的转发,以及protocols的转发,这里的msgcode就是protocol的(offset, offset+length) (注意这里NewPeer的驱动的是protocol 对一个模拟的conn写和读,并没有真正的网络conn,为了测试用,newPeer才是真正被用到的,conn是SetupConn创建来的 server.go的conn结构其中transport是rlpx.go newRLPX生成的)
node/node.go
config.go 主要是一些配置其中node里边有网络节点的一些配置,还有一个account(账户)的配置
message.go 定义了p2p交互中的msg和一系列封装 eofSignal 消息字节读取没了给chan中发信号 MsgPipe一个双向交互的消息pipe msgEventer 收发消息时给feed发送一个事件
api.go geth console 到处的js api都在这里
nat目录下的nat.go natpmp.go natupnp.go主要是实现nat,也就是让外网的机器可以访问内网的机器有upnp协议pmp协议还有直接ip出去的 nat.map函数就是把内网的端口映射到外网去
p2p/metrics.go 主要是封装了一层conn 用作仪表,其实就是记录下进出流量的多少
p2p/rlpx.go 是对conn 发送之前加密,和rlp(加解码)和conn作用一样
server.go 表示p2p协议,通过udp协议找到table节点pingpong,然后通过tcp协议真正链接生成peer 其中inboundConn表示链接进来的(本地通过accept的node)而没有这个标记是本地主动dial的节点,tcp和udp都有自己的pingpong,udp的在udp.go tcp的在peer.go
rpc=======================================
subscription.go 维护Notifier主要是Notify函数通过创建codec.CreateNotification并且codec.Write来发消息
rpc整体来说就是通过json等方式调用注册的服务函数,而且通过映射的方式获取函数名等等match到json上
rpc end***************************************************************
node========================================
node.go 代表了一个节点,内含一个p2pserver 和rpcserver 主要的执行是通过Register注册的服务
node end**************************************************************
trie===========================================
node 分为几类 shortnode 主要是一个前缀key+node fullnode是16个字母(key)+一个通用字母 每一个字母对应一个node valnode直接就是叶子节点,还有一类hashnode 是node的hash值,存到db中去了
trie.go 一个树结构有几种节点,其中类型参考node的讲解,其中 hash 这个函数是把节点都存到db里面并且返回(node的hashnode,node的拷贝,err) Commit目前分析就是hash函数的调用过程,主要是把节点存db然后转换成node
hasher.go 用户hash node,并且将hash存在node 的flag里面,或者存到db中去,用的hash算法是keccak256
secure_trie.go 对trie.go中的节点简单的封装和转查
trie就是以key和value的形式构造了一颗用key做索引的树,key被做成了shortnode和fullnode,然后提供了一个hash函数,这个函数递归的hash子节点,最后算出来一个hash根(有一个用途就是blockheader里头的tx hash)
proof.go 提供两个关键函数 prove 通过key生成一条路径的证明,VerifyProof 提供证明和key 验证是否达标 但是具体的验证原理还没有搞的特别明白
tire end************************************************************************
types ============================================
transaction.go 代表了一个tx 内部还有一个msg,这里面有通过price排序的heap 需要注意的是签名中可以获取from地址所以tx的结构中只有个to的字段
receipt.go 代表了tx的执行结果,主要有poststate status 和CumulativeGasUsed(累积使用的gas)一个bloom filter以及logs组成
transaction_signing.go 对tx做签名,从签名中获取到chainId以及sender(from)
block.go 定义一个区块,区块包含几个部分一个是header 一个是uncle headers 一个是tx
types end************************************************************************
genesis.go 主要是生成genesis block 其中SetupGenesisBlock生成了一个genesis block 并且把相关的信息存放在db中去,比如config信息
les ================================================
peer.go 定义了一个peer和peerset,前者主要用于各种收发协议消息,后者是peer的set,用于对peer进行peerSetNotify的注册和反注册
execqueue.go 里面有一个func的queue,在一个单独的go线程执行
protocol.go 定义了 announceData 有点像比特币中的header消息,用于从远方拉block的消息,并且提供了 announceData 的 sign和验证的函数sign和checkSignature
distributor.go 主要是distPeer(其实就是peer)和distReq两个类型的协同工作 distReq代表一个请求,主要是request函数返回了一个func,这个func可以放到queueSend中去而requestDistributor就是loop distReq队列然后对其进行queueSend,而queue就是对外的函数,用于放进队列distReq进而进入loop发送请求,发送完以后通过queue的返回值(一个chan)返回一个peer(dispeer接口对应的对象是peer)
flowcontrol 感觉是通过上次发送的时间和对面的handsake 得到的一些buflimit等信息来决定下次发送需要多久,以让各个节点都有时间处理,减少发送失败的情况(猜的哈)
txrelay.go 一个tx消息的中继器,主要是peer.go distributor.go两个逻辑的结合,选取peer发送distReq进而发送tx
serverPool.go 是一个链接池对应的是p2p的peer,维护了这么一个链接池connect是链接进来的peer调用 registered是完成handshake调用, 其中newQueue代表connect进来的knownQueue表示已经完成registered,其中start函数的两个参数可以看出一个server表示启动p2p开始做dial链接,而topic是一个discorver逻辑发现节点用的对应的是udp发现节点通过tcpserver进行dial连接
retrieve.go 主要是有 distributor.go 用于把消息发送出去(retrieve函数) 然后通过自身提供的函数deliver分发到应答的消息到pee
odr_requests.go 代表了集中request 和他们的接口函数,其中每一种类型的req会使用peer里边的函数发送,另外每一种类型req都有一个validate函数是在retrive.go的deliver函数中使用用于验证消息的合法性
odr.go 主要是Retrieve函数收到一个req(odr_requests.go)封装了一下转发出去,另外封装了一些indexer(chain_indexer.go postprocess.go)这个Retrieve 函数不但把req发出了而且还把结果取回来通过req的StoreResult或者Validate把结果存到db或者自身去了,这里要注意
handler.go 主要是处理消息然后转发给 retrieve.go解析的,同时也响应部分消息,比如要求发送block的消息,直接通过 peer发送出去
fetcher.go 目前理解是一个记录了各个peer有多少个node header然后定期从各个peer上获取东西到本地的一个类
les end********************************************************************************
light================================================
postprocess.go是对chain_indexer.go的一个封装填充了backend NewBloomTrieIndexer NewChtIndexer 两个分别是两个backend
lightchain.go 这个文件中实现了一个链的逻辑 InsertHeaderChain这个函数比较重要就是把参数传进来的一系列的header写到数据库中(先要验证)想法连到主链上,如果连不到就连的侧链上,然后通过chainSideFeed和chainHeadFeed通知外部链接的情况,通过SubscribeChainHeadEvent和SubscribeChainSideEvent,和SubscribeChainEvent来监听
trie.go一个封装了一个trie的结构(odrTrie)),如果发现本地db有这个trie的内容就直接返回,如果没有就循环的通过Retrieve函数从别的地方获取
txpool.go 目前了解有点类似比特币中的mempool,是一个tx的缓存池,加入到池子中有一些简单的验证,比如验证nonce和gas值对不对,然后放到pending的map中,并通过txrelay转发出去,如果有些block被链接到链上了还会标记这些penging的tx已经mined或者block被从链上退回来了,也会放到rollback列表中去,然后告诉txrelay去处理一些发送的pending节点
另外通过SubscribeNewTxsEvent来通知外部有新的tx加入到pool中,它自身又通过SubscribeChainHeadEvent监听了lightchain关于head的事件
light end********************************************************************************
core/state=============================================
这个目录里面就是以太坊api实际作用到磁盘上的东东,stateobject就是以太坊的状态对象,包含了对一个账户余额等的操作以及key-value对的设置以及code的设置这三部分
journal.go算是一个回滚列表,容易拍一个state快照然后回滚,对state数据库的回滚操作
statedb.go 是state_object的集合通过addr 为key的map, 自己也有trie存放的是state_object
state_object.go 维护了一个stateObject对象 主要是getstate setstate 和账户balance维护以及codeshash的维护,内部有一个节点的trie存放的key-value对都是hash,最后这些都不会被放到db中
core/state end***********************************************************************
core/type =============================================
receipt CumulativeGasUsed代表了一个block到这个tx的时候用了多少的gas,而gasused代表这一个tx用了多少的gas, receipt目前我理解的有几个用途,一个是记录了一个PostState字段代表了走到这个tx的时候状态数据库的roothash是什么,更方便确定一个tx的状态确认,另外是Bloom可以快速的查看一个日志是否在这个tx中,并且有logs这个字段,这里还有一个疑问或者注意点,就是虚拟机有些错误有了,但是还是会收录到tx里边所以receipt有个Status来代表vm是否执行成功,其中我目前理解的有些错误如gas用完了之类的,虽然是错误但是这个tx依然反映到了db中去,gas不还了哈哈
(todo vm执行失败,看看vm内部会不会回滚一点db,反正部分失败是在外部是不会回滚db的)
todo vm执行失败,看看vm内部会不会回滚一点db,反正部分失败是在外部是不会回滚db的 ?,在vm/evm.go这个文件的call函数中都有回滚
而且其实ErrInsufficientBalance这个错误(也就是转账以太坊失败的这种错误),是不会写入区块链,而别的错误vm/error.go,虽然状态会被回滚(vm/evm.go这个文件的call函数)但是会写入区块链的,并且返回receipt
core/type end*************************************************************************
core================================================
chain_indexer.go eventLoop等有新的head 出现的时候(目前来看是从headerchain过来的,也就是有head被连到主链时候发送消息过来)收到一个event 然后调整本地有可能是换链最新的head(head的seg) updateLoop 是等待更新消息(eventloop在发现有新的section的时候发送),然后调用processSection 进而调用backend整块的处理section中的head
section的概念有点类似比特币中的难度一段一段的那种
chainDb 存放的是实际的数据
indexDb 存放的是index的数据(index就是section的一些信息,如一个section的头是哪个blockheader)
children 存放的是一些子的chainindexe
backend 后台处理程序接口,主要是reset Process 和commit三个函数,这一套函数处理一个section reset接收section num + 上一个section的head, process循环调用处理本section中的head,commit用于提交一般用于写入数据库
headerchain.go 一个代表header链的结构,其中currentheader 是链头,Canonical这个单词表示是正链,其他的函数都是只存数据库不是主链的意思比如WriteCanonicalHash表示在朱脸上写hash WriteHeadHeaderHash就只是存了一下
state_transition.go 这里有tx执行的过程调用包括虚拟机的执行,尤其是gas使用的一些逻辑,不过这里目前有一个疑问为啥退款是使用min(gas的一半,剩余的gas)也就是有封顶,而且如果达到封顶值以后一些钱就消失了没有给矿主,矿主只拿到了gasused
state_processor.go 目是对block中的tx运行虚拟机生成receipt和state等
blockchain.go 真正的链操作,通过downloader等把块数据传到这里如InsertChain,然后通过验证块(state_processor.go的 Process等)加入区块
block_validator.go 应该是主要验证一些block的body信息
core end********************************************************************************
consensus/ethash========================================
ethash.go 目前看明白的是两个hash cache 一个就叫cache一个叫dataset,这两个包含到了ethash这个结构中,epoch这个是一个区段的意思,一个区段有一个cache和dataset
consensus.go 共识engine,主要是看header uncle等结构以及难度确定和验证用的 VerifySeal表示验证了header的难度其中header的MixDigest是一个另类的头的hash 这个和比特币不一样相当于存了一个hash到header中 verifyHeader检查header的各个字段如果seal参数为true就检查难度是否达标
consensus/ethash end*****************************************************************
eth/downloader=============================================
downloader.go 主要是通过网络下载block和header和receipt三种信息然后通过chain放到链中去
queue.go 一个队列通过retrievexxx和deliverxxx两个类型的函数排队下载最后放结果,deliver函数通过一个haderProcCh把结果高出去
eth/downloader end************************************************************************
consensus/ethash============================================
sealer.go Seal 通过提供这个函数挖矿
consensus/ethash*************************************************************************
miner====================================================
agent.go负责挖hash,然后把成的模块给worker.go
worker.go负责从txpool中生成block,然后形成work给agent算hash 生成block以及验证block验证tx的逻辑都在这里
miner.go是对worker的一个封装
worker 的commitNewWork把区块准备好(验证什么的)但是没有算hash等
agent 的mine实际算hash
Worker wait函数把挖矿好的block插入到放到链中WriteBlockWithState
另外一个区块的目标难度和几个因素有关系一个是和上一个区块的隔离时间,一个是上一个区块有几个uncle,还有一个是上一个区块的难度
miner end**********************************************************************************
vm======================================================
contract.go 抽象了一个智能合约,有caller 有自己的code 有合约带的gas等
contracts.go 定义了一个map代表里系统合约PrecompiledContract
ecrecover系统合约从输入(输入中包含了sig和hash)中解出来了一个公钥
sha256hash系统合约进行了一个sha256的hash计算
ripemd160hash系统合约进行一次ripemd160hash的计算
dataCopy系统合约直接返回输入数据
bigModExp 对一段数据做exp操作具体的看bigint 的exp函数
bn256Add 对bn256 做+操作
bn256ScalarMul 对bn256做*操作
bn256Pairing 对bn256做pairing操作
opcodes.go 所有指令的定义
analysis.go codeBitmap表示一段代码中的数据,生成一个bitmap,如果这个位置为1表示这一个字节是数据否则是代码 destinations是一个code hash到codebitmap的map,has函数看看索引上的指令是不是jmpdest指令
jump_table.go 定义了指令的operation每一个指令对应一个operation结构主要包含 executionFunc执行函数gasFunc gas消耗函数 stackValidationFunc堆栈检查函数 memorySizeFunc内存消耗函数
instructions.go 指令的执行函数都在这里 executionFunc
opAdd opSub opMul opDiv --- opSAR 基本运算,把栈顶相关运算数取出来然后将结果放到堆栈去
opSha3 从堆栈中获取内存的地址(offset+size)从内存中取出来数据,然后做keccak256处理,然后把算出来的hash入栈,有一个梗是会把(hash,data)当作preimage写到数据库中
opAddress 获取contract.Address字段
opBalance 把堆栈顶端的地址换成了该地址对应的余额
opOrigin 代表evm执行程序的最开始的caller地址
opCaller 这个合约的calle
opCallValue 堆栈中push contract.value
opCallDataLoad 栈顶拿一个地址(offset)从input中获取一个32位的数,放到栈顶
opCallDataSize 栈顶放一个input的size
opCallDataCopy 把input内的内容copy到内存(栈顶有三个参数内存offset, input offset 和len)
opReturnDataSize 栈顶push interpreter的returnData的size
opReturnDataCopy 把 interpreter的returnData 的部分内容copy到内存(栈顶三个参数 内存offset returndata的offset和size)
opExtCodeSize 栈顶的code地址转换成codesize
opCodeSize 把合约自己的codesize放到栈顶
opCodeCopy 合约自己的code部分内容copy到内存中(内存offset, code offset, size)
opExtCodeCopy 把一段代码的部分copy到内存中(code的地址,内存offset, code offset, size)
opGasprice 获取gas的价格evm的GasPrice参数
opBlockhash 把栈顶的block num转换成block hash
opCoinbase 把coinbase的地址放到栈顶
opMload 栈顶给一个内存地址取出来一个32字节数据放到栈顶
opMstore 存一个地址到内存
opSload 将栈顶的key转换成对应的状态value
opSstore 设置对应的key,value
opJumpi 通过栈顶的跳转目标和条件变量决定是否跳转到pc
opPc 把栈顶放上pc的值
opMsize 栈顶放上内存现在的大小
opGas 把合约当前的gas放到栈顶
opCreate 创建一个合约,参数为给合约的转账,合约地址(内存offset 和size),创建合约后消耗gas并把执行结果也就是合约的地址写到堆栈
opCall 调用合约把返回值写到内存中指定的位置,并且会在堆栈中写入0,1代表调用是否成功
opCallCode 调用合约把返回值写到内存中指定的位置,并且会在堆栈中写入0,1代表调用是否成功只不过这个调用用的toaddr和calladdr一样(但是code是被调用合约的)
opDelegateCall 同上调用的函数是DelegateCall
opStaticCall 同上调用的函数是StaticCall
opSuicide 获取到当前合约的balance然后转账到堆栈顶端的地址
opRevert 直接回滚返回错误errExecutionReverted,这个逻辑在interpreter.go
gas_table.go gas的消耗函数都在这里 gasFunc
stack_table.go stackValidationFunc 堆栈检查函数在这里
memory_table.go 内存消耗函数都在这里 memorySizeFunc
stack.go 一个堆栈结构
intpool.go 封装了一个stack
interpreter.go 主要是一个run函数(注释里面有句话很重要,说是所有返回的err都是回滚+消耗掉所有gas除了errExecutionReverted表示回滚但是不消耗gas) 主要是运行一个合约
memory.go 代表内存的一个结构就是setvalue和getvalue之类的函数
合约里的input是做什么用的?, 其实就是opCallDataLoad通过堆栈中的一个input的偏移来找一个big32大小的数据并且放到堆栈中去
intpool又是做什么的?感觉就是一个普通的bigint的缓存池,没有实际意义因为所有的get操作都直接被set成另外一个值了
call和callcode等的区别是什么?
call callcode 和DelegateCall这三个的区别主要是三个合约的成员变量
1、caller代表了自己调用者 2、self代表了自己 3、value代表合约里有多少钱
这三个又可以归结为两个一个是状态数据库(key value)一个是合约里有多少钱
状态数据库只能写当前合约地址的也就是self的keyvalue,所以self影响了状态数据库
合约里转账也只能转合约的value其实还是对应的self这个地址(因为value对应的是数据库里的地址的,contract.value仅仅是查询使用的),所以这里会影响value(opCreate和opSuiside)
而self在调用别的合约的时候就变成了caller了,所以self会影响calle
这三种调用
call 是caller是传入的参数caller,self用的传入的参数to,value用的是传入的value,并且真实的把caller的value转账到了to,并且如果这个合约地址不存在新建一个合约账户,这样写的数据库是to的数据库,用的钱是caller转给他的钱
callcode 是caller是传入的参数caller,self用的传入的参数caller,value用的是传入的value,但是由于caller和self都是caller所以没有实际转账,这样写的数据库是caller的数据库,用的钱是caller的钱
DelegateCall caller是caller的caller,self是caller本身,value为caller的caller的value,因为是代理调用所以其实不用转账,这样写的数据库是caller的数据库,用的前是caller的钱,其实这个合约调用就相当于重新建了一个caller的合约(除了code不一样其他都一样)
vm end*************************************************************************************
params===================================================
gas_table.go 一些gas定价的表格参数
params*************************************************************************************
问题
每一个peer有一个pubkey这个是从哪儿来的?目前理解为p2p加密通信协议,用于对消息进行加密,每一个peer的nodeid就是pubkey,handshake的时候他们用公私密钥进行加解密
todo vm执行失败,看看vm内部会不会回滚一点db,反正部分失败是在外部是不会回滚db的 ?,在vm/evm.go这个文件的call函数中都有回滚
receipt在哪儿存?(通过blockchain.go的WriteBlockWithState写到数据库) block中只有receipt的hash,以及为啥要设计他?他有啥用?他有啥用目前来看是只有block数据和receipt数据是在网络上传输的,而state数据是没有传输的
还有从一个地方可以看出来receipt用途的一个小点,就是api.go中的GetTransactionReceipt会把所有的receipt字段重新封装了一些字段给到客户端
合约账户的私钥在哪儿?合约的地址是通过caller的地址加上caller的nonce生成的,不是通过私钥生成的,所以没有私钥,也就不存在 私钥在哪儿的这个问题(evm/evm.go create函数)但是引出来一个问题,对应合约的钱是通过什么机制转出去的?这里的答案是opSuicide 这个指令也就是只有合约自己可以把钱转出去,其实还有一个opcreate是在创建子合约的时候也会转账,另外还有一个方式是调用opcall指令(对应的是addr.transfer这个函数)
对比构造函数和一般的函数调用对应的指令因为看源码vm/evm.go的代码好像调用都是从code的第一个字节开始执行的?这里不知道有啥蹊跷没有 (in *Interpreter) Run的起始pc都是0,这个可以看以太坊技术详解与实战第五章
以太坊中的虚拟机执行的err有的没有回滚原因是什么?
以太坊中的挖完的区块怎么连接到链上,也就是对应比特币中的区块候选链在哪儿 WriteBlockWithState 挖完矿直接进这里了
其他备忘:
uncle的选取是,找自己7个以上的祖先,和然后找这7个祖先对应的uncle,自己添加的uncle必须是7个祖先的后代(也就是uncle老爹必须是在7代主线内),而且必须不在7个祖先的uncle里
挖矿的奖励目前看只和uncles数有关系(固定阶段的两个分支也有区别)没有像比特币一样和时间有关系
abi调用过程目前知道的是发送调用
经过(b *bridge) Send(bridge.go)->(c *BoundContract) Call(abi/bind/backends/base.go)-> (b *SimulatedBackend) callContract(simulated.go)->(st *StateTransition) TransitionDb()(state_transition.go)
返回值通过TransitionDb返回然后abi通过unpack把他转换成json最后返还给客户端
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。