python ethereum 代码分析 《2》
python 版本以太坊
pyethapp 模块
本章主要介绍pyethapp 模块中的ChainService 和 PoWService
一些关键概念
totalDifficulty 总难度:
total difficulty总难度是当前某条链所有区块难度的总和,total difficulty被用来指示最长的那一条链,某个节点如果想从其他节点同步数据的话,他会选择total difficulty最大的那一条链来同步数据。值得注意的是uncle block的difficulty也被计算到totalDifficulty中去,以太坊白皮书中的幽灵协议阐述了为什么这么设计
uncle block 叔叔区块:也是矿工挖出来的区块,它也是合法的,但是发现的稍晚,或者是网络传输稍慢,而没有能成为最长的链上的区块
以太坊十几秒的出块间隔,大大增加了孤块的产生,并且降低了安全性。通过鼓励引用叔块,使引用主链获得更多的安全保证(因为孤块本身也是合法的)
区块可以不引用,或者最多引用两个叔块
叔块必须是区块的前2层~前7层的祖先的直接的子块
被引用过的叔块不能重复引用
引用叔块的区块,可以获得挖矿报酬的1/32,也就是5*1/32=0.15625 Ether。最多获得2*0.15625=0.3125 Ether
参考资料http://blog.csdn.net/superswords/article/details/76445278
https://zhuanlan.zhihu.com/p/28928827
head_candidate 候选头区块: 矿工本地的候选头区块,相当于一个临时区块,head_candidate一直存在并且矿工会一直更新。矿工将交易打包进这个区块,计算出随机数后将次区块作为新区块广播出去。
contract :合约,本身也是一个账户。每当一个交易指向一个合约账户时,该交易transaction的data属性作为该合约的输入执行该合约。
以太坊基本的一些概念:http://ethdocs.org/en/latest/contracts-and-transactions/index.html
ChainService 和 PowService的关系
chain service负责区块链的同步和更新,处理连接的各个节点的eth_protocol数据包,以及交易和区块的广播; pow service是矿工挖矿的服务,计算出’幸运值’后告知chain service,chain service 将区块写入区块链并广播出去。
@property
def head_candidate(self):
if self._head_candidate_needs_updating:
self._head_candidate_needs_updating = False
# Make a copy of self.transaction_queue because
# make_head_candidate modifies it.
txqueue = copy.deepcopy(self.transaction_queue)
#将交易打包,引用uncle区块,执行交易中的合约,更新区块状态
self._head_candidate, self._head_candidate_state = make_head_candidate(
self.chain, txqueue, timestamp=int(time.time()))
return self._head_candidate
def _on_new_head(self, block):
log.debug('new head cbs', num=len(self.on_new_head_cbs))
self.transaction_queue = self.transaction_queue.diff(
block.transactions)
self._head_candidate_needs_updating = True
#cb是pow service的回调函数mine_head_candidate,更新head_candidate并开始挖矿
for cb in self.on_new_head_cbs:
cb(block)
def mine_head_candidate(self, _=None):
#打包当前交易队里中的交易并更新head_candidate
hc = self.chain.head_candidate
if not self.active or self.chain.is_syncing:
return
elif (hc.transaction_count == 0 and
not self.app.config['pow']['mine_empty_blocks']):
return
log.debug('mining', difficulty=hc.difficulty)
#开始挖矿
self.ppipe.put(('mine', dict(mining_hash=hc.mining_hash,
block_number=hc.number,
difficulty=hc.difficulty)))
计算出幸运值后,调用chain.add_mined_block写入区块链并广播
#成功找到幸运值
def recv_found_nonce(self, bin_nonce, mixhash, mining_hash):
log.info('nonce found', mining_hash=mining_hash.encode('hex'))
#再次打包交易,更新head_candidate
block = self.chain.head_candidate
if block.mining_hash != mining_hash:
log.warn('mining_hash does not match')
return False
block.header.mixhash = mixhash
block.header.nonce = bin_nonce
#添加新块并广播
if self.chain.add_mined_block(block):
log.debug('mined block %d (%s) added to chain' % (
block.number, encode_hex(block.hash[:8])))
return True
else:
log.debug('failed to add mined block %d (%s) to chain' % (
block.number, encode_hex(block.hash[:8])))
return False
def add_mined_block(self, block):
log.debug('adding mined block', block=block)
assert isinstance(block, Block)
#添加新块
if self.chain.add_block(block):
log.debug('added', block=block, ts=time.time())
assert block == self.chain.head
self.transaction_queue = self.transaction_queue.diff(block.transactions)
self._head_candidate_needs_updating = True
#广播新块
self.bro