本文初步分析了一个交易在以太坊内部的处理流程,涉及到交易的接收,检查,执行,同步,区块的构建以及挖矿,结合前面一篇基于黄皮书的理解总结,对以太坊有了更多的认识。因为主要的工作在c++层面,所以这里以c++版本的以太坊源码作为学习资源。

如有理解错误的地方,希望看到的人不吝赐教,共同学习,谢谢。

1. 发送交易 eth_sendTransaction

// 一个交易框架的构成,实际就是一个交易构成元素
struct TransactionSkeleton
{bool creation = false;Address from;Address to;u256 value;bytes data;u256 nonce = Invalid256;u256 gas = Invalid256;u256 gasPrice = Invalid256;std::string userReadable(bool _toProxy, std::function<std::pair<bool, std::string>(TransactionSkeleton const&)> const& _getNatSpec, std::function<std::string(Address const&)> const& _formatAddress) const;
};string Eth::eth_sendTransaction(Json::Value const& _json)
{try{// 从Json结构中构建交易骨架TransactionSkeleton t = toTransactionSkeleton(_json);setTransactionDefaults(t);......// 向客户端提交这个交易h256 txHash = client()->submitTransaction(t, ar.second);// 返回交易hashreturn toJS(txHash);......}....
}

学习中省略了很多代码,有些是辅助性的,有些可能是功能性,这里的目的是梳理一个流程,建立一个框架印象,省略的代码可以后续继续学习

这里很简单,就是从json中构建了一个交易框架,然后传给了客户端

2. 接收交易 importTransaction

h256 Client::submitTransaction(TransactionSkeleton const& _t, Secret const& _secret)
{// 对交易框架 缺省字段进行补充,最后行程一个交易TransactionSkeleton ts = populateTransactionWithDefaults(_t);ts.from = toAddress(_secret);Transaction t(ts, _secret);// 把交易添加到交易队列中return importTransaction(t);
}// 添加交易到交易队列中
h256 Client::importTransaction(Transaction const& _t)
{// Client继承于Worker,准备线程处理交易列表prepareForTransaction();// Use the Executive to perform basic validation of the transaction// (e.g. transaction signature, account balance) using the state of// the latest block in the client's blockchain. This can throw but// we'll catch the exception at the RPC level.// 这里借用了Executive的initialize来做基本的交易合法性验证// 实际上Executive就是一个交易真正执行的主体,Evm computation,后面还会看到Block currentBlock = block(bc().currentHash());Executive e(currentBlock, bc());e.initialize(_t);// 将交易加入到交易队列中ImportResult res = m_tq.import(_t.rlp());......// 最后返回交易的hash值return _t.sha3();
}

客户端收到交易后,借用 Executive 初步检验了交易构成的合法性,然后加入到自己的交易池(m_tq)中,后面就继续跟踪m_tq是被如何处理的呢?

3. 处理交易
前面说 Client是Worker的子类,启动了处理交易的线程

Client::prepareForTransaction --> void startWorking() { Worker::startWorking(); }; --> Worker::workLoop()void Worker::workLoop()
{while (m_state == WorkerState::Started){if (m_idleWaitMs)this_thread::sleep_for(chrono::milliseconds(m_idleWaitMs));// 等待一段定时时间后 缺省30ms,执行工作doWork();}
}void Client::doWork(bool _doWait)
{......// 同步交易队列,实际内部处理内容比较多syncTransactionQueue();tick();// 开始挖矿 POWrejigSealing();.....
}

交易处理到区块处理分了两大步,一个就是同步交易,实际也执行了交易,然后是进行挖矿,计算POW,下面分开来看

3.1 交易同步及执行

void Client::syncTransactionQueue()
{......// 这里构建了区块 m_working, 在Block.sync中会执行交易,执行完后,这个m_working就是我们后面要验证的区块了// 这里就涉及上上面提到过的 Executive 对象了,它是交易的真正执行者,内容也比较复杂,后面看合适位置再详细记录下// 这里的主要过程罗列一下:// Block::sync --> ExecutionResult Block::execute(...) --> m_state.execute --> State::executeTransaction ---> _e.initialize  _e.execute  _e.finalize//                                                     --> m_transactions.push_back(_t); // 交易执行完 加入到交易列表中//                                                     --> m_receipts.push_back(resultReceipt.second);tie(newPendingReceipts, m_syncTransactionQueue) = m_working.sync(bc(), m_tq, *m_gp);}......m_postSeal = m_working;DEV_READ_GUARDED(x_postSeal)// 更新 bloomer filterfor (size_t i = 0; i < newPendingReceipts.size(); i++)appendFromNewPending(newPendingReceipts[i], changeds, m_postSeal.pending()[i].sha3());// Tell farm about new transaction (i.e. restart mining).onPostStateChanged();// Tell watches about the new transactions.noteChanged(changeds);// Tell network about the new transactions.// 在网络上同步交易,这里跟下去,就会到 P2P网络部分 session host 这些都可以看到了// 这里置位host的标记位 m_newTransactions// 到了host内部会有如下流程:// Host::startedWorking --> h.second->onStarting()(EthereumCapability::onStarting()) --> EthereumCapability::doBackgroundWork()  这里就看到了客户端下面这个函数职位的标记位// 然后 EthereumCapability::maintainTransactions() --> m_host->sealAndSend --> Session::sealAndSend --> Session::send --> Session::write()if (auto h = m_host.lock())h->noteNewTransactions();......
}

上面再代码中注释了很多,每一步可以顺着看到很多内容。

## 交易执行

前面总结了一个交易在以太坊中的整个流程,中间一笔带过了交易的执行主体 Executive ,今天补上这一块

交易执行时交给 State来完成的,State is Model of an Ethereum state, essentially a facade for the trie,即以太坊状态模型,本质上就是 state trie的外在表现,允许查询账户状态,以及创建账户和修改账户

仔细看下State.h 会发现这就是以太坊世界状态的操作接口,连接了DB,State,Cache,Accounts,Balance, Code,里面注释很清楚,很容易理解函数的作用,剩下的就看怎么调用起来的。

// 交易在以太坊内部执行的入口,主要是通过Executive来完成
std::pair<ExecutionResult, TransactionReceipt> State::execute(EnvInfo const& _envInfo, SealEngineFace const& _sealEngine, Transaction const& _t, Permanence _p, OnOpFunc const& _onOp)
{// Create and initialize the executive. This will throw fairly cheaply and quickly if the// transaction is bad in any way.//创建一个 executive实例,Executive e(*this, _envInfo, _sealEngine);// 日志相关ExecutionResult res;e.setResultRecipient(res);.....// 区块目前已用的gas数量u256 const startGasUsed = _envInfo.gasUsed();// 执行交易,后续接着看这个bool const statusCode = executeTransaction(e, _t, onOp);// 交易执行完,清理工作,revert或者清理掉空账户bool removeEmptyAccounts = false;switch (_p){case Permanence::Reverted:m_cache.clear();break;case Permanence::Committed:removeEmptyAccounts = _envInfo.number() >= _sealEngine.chainParams().EIP158ForkBlock;commit(removeEmptyAccounts ? State::CommitBehaviour::RemoveEmptyAccounts : State::CommitBehaviour::KeepEmptyAccounts);break;case Permanence::Uncommitted:break;}// 更新日志TransactionReceipt const receipt = _envInfo.number() >= _sealEngine.chainParams().byzantiumForkBlock ?TransactionReceipt(statusCode, startGasUsed + e.gasUsed(), e.logs()) :TransactionReceipt(rootHash(), startGasUsed + e.gasUsed(), e.logs());return make_pair(res, receipt);
}

流程比较清楚,1.创建交易执行体 executive,2. 执行交易, 3. 收尾工作  4. 日志更新

执行交易

/// @returns true when normally halted; false when exceptionally halted; throws when internal VM exception occurred.
bool State::executeTransaction(Executive& _e, Transaction const& _t, OnOpFunc const& _onOp)
{// save point, 记录一下当前changelog的位置,为了以后出现异常后的回滚// changelog 是一个vector,记录所有账户的状态改变,他的size位置即记录点// changelog 可以通过 savepoint() rollback() commit() 来操作size_t const savept = savepoint();try{ // 初始化交易 主要是校验一个交易的合法性_e.initialize(_t);// 执行交易, 返回true,执行finalize,返回false则执行go,如果执行有异常,执行evm的rollbackif (!_e.execute())_e.go(_onOp);return _e.finalize();}catch (Exception const&){rollback(savept);throw;}
}

这里的流程也比较清楚,记录回滚标记点,校验交易,执行交易,检查交易结果:成功 失败 异常,依次看下执行过程

校验交易

void Executive::initialize(Transaction const& _transaction)
{// 记录交易m_t = _transaction;// 根据区块号,选择使用哪个evm调度策略 EVMSchedule 设定好了调度相关的数据,例如某个步骤的gas是多少都是在这里面定义好的,根据不同的区块号阶段,可能有些不同,这里就是取到正确的EVM配置m_baseGasRequired = m_t.baseGasRequired(m_sealEngine.evmSchedule(m_envInfo.number()));try{// 校验一部分交易参数的合法性m_sealEngine.verifyTransaction(ImportRequirements::Everything, m_t, m_envInfo.header(), m_envInfo.gasUsed());}catch (Exception const& ex){m_excepted = toTransactionException(ex);throw;}if (!m_t.hasZeroSignature()){// Avoid invalid transactions.u256 nonceReq;try{nonceReq = m_s.getNonce(m_t.sender());}catch (InvalidSignature const&){m_excepted = TransactionException::InvalidSignature;throw;}// 交易的nonce是否跟发起交易的账户的nonce相同if (m_t.nonce() != nonceReq){m_excepted = TransactionException::InvalidNonce;BOOST_THROW_EXCEPTION(InvalidNonce() << RequirementError((bigint)nonceReq, (bigint)m_t.nonce()));}// 交易需要的gas是否超过了账户本身的balance// Avoid unaffordable transactions.bigint gasCost = (bigint)m_t.gas() * m_t.gasPrice();bigint totalCost = m_t.value() + gasCost;if (m_s.balance(m_t.sender()) < totalCost){m_excepted = TransactionException::NotEnoughCash;m_excepted = TransactionException::NotEnoughCash;BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(totalCost, (bigint)m_s.balance(m_t.sender())) << errinfo_comment(m_t.sender().hex()));}m_gasCost = (u256)gasCost;  // Convert back to 256-bit, safe now.}
}

这里首先选择了EVM配置,然后校验交易参数,入参是ImportRequirements::Everything

//
void SealEngineFace::verifyTransaction(ImportRequirements::value _ir, TransactionBase const& _t,BlockHeader const& _header, u256 const& _gasUsed) const
{if ((_ir & ImportRequirements::TransactionSignatures) && _header.number() < chainParams().EIP158ForkBlock && _t.isReplayProtected())BOOST_THROW_EXCEPTION(InvalidSignature());if ((_ir & ImportRequirements::TransactionSignatures) &&_header.number() < chainParams().experimentalForkBlock && _t.hasZeroSignature())BOOST_THROW_EXCEPTION(InvalidSignature());if ((_ir & ImportRequirements::TransactionBasic) &&_header.number() >= chainParams().experimentalForkBlock && _t.hasZeroSignature() &&(_t.value() != 0 || _t.gasPrice() != 0 || _t.nonce() != 0))BOOST_THROW_EXCEPTION(InvalidZeroSignatureTransaction() << errinfo_got((bigint)_t.gasPrice()) << errinfo_got((bigint)_t.value()) << errinfo_got((bigint)_t.nonce()));if (_header.number() >= chainParams().homesteadForkBlock && (_ir & ImportRequirements::TransactionSignatures) && _t.hasSignature())_t.checkLowS();eth::EVMSchedule const& schedule = evmSchedule(_header.number());// Pre calculate the gas needed for executionif ((_ir & ImportRequirements::TransactionBasic) && _t.baseGasRequired(schedule) > _t.gas())BOOST_THROW_EXCEPTION(OutOfGasIntrinsic() << RequirementError((bigint)(_t.baseGasRequired(schedule)), (bigint)_t.gas()));// Avoid transactions that would take us beyond the block gas limit.if (_gasUsed + (bigint)_t.gas() > _header.gasLimit())BOOST_THROW_EXCEPTION(BlockGasLimitReached() << RequirementErrorComment((bigint)(_header.gasLimit() - _gasUsed), (bigint)_t.gas(),string("_gasUsed + (bigint)_t.gas() > _header.gasLimit()")));
}

执行交易

bool Executive::execute()
{// Entry point for a user-executed transaction.// 根据注释可以明白 这个就是用户触发的交易的真正入口了// Pay...// 这里就是两种交易类型了// 扣掉交易消耗m_s.subBalance(m_t.sender(), m_gasCost);assert(m_t.gas() >= (u256)m_baseGasRequired);if (m_t.isCreation())// 1. 合约创建return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)m_baseGasRequired, &m_t.data(), m_t.sender());else// 2. 消息调用return call(m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)m_baseGasRequired);
}

这里分了两步,也就是ethereum的两种消息类型:合约创建和消息调用,这里的判断条件isCreate() 是在上面文章提到的jsonrpc最初入口的时候就确定了的。

TransactionSkeleton toTransactionSkeleton(Json::Value const& _json)
{......// to 为空即为合约创建,否则就是消息调用if (!_json["to"].empty() && _json["to"].asString() != "0x" && !_json["to"].asString().empty())ret.to = jsToAddress(_json["to"].asString());elseret.creation = true;......
}h256 Client::submitTransaction(TransactionSkeleton const& _t, Secret const& _secret)
{......// 构造交易Transaction t(ts, _secret); ......
}Transaction(TransactionSkeleton const& _ts, Secret const& _s = Secret()): TransactionBase(_ts, _s) {}TransactionBase::TransactionBase(TransactionSkeleton const& _ts, Secret const& _s):// 这里记录到交易参数中,是否为合约创建m_type(_ts.creation ? ContractCreation : MessageCall),m_nonce(_ts.nonce),m_value(_ts.value),m_receiveAddress(_ts.to),m_gasPrice(_ts.gasPrice),m_gas(_ts.gas),m_data(_ts.data),m_sender(_ts.from)
{if (_s)sign(_s);
}

合约创建

bool Executive::create(Address const& _txSender, u256 const& _endowment, u256 const& _gasPrice, u256 const& _gas, bytesConstRef _init, Address const& _origin)
{// 如果消息调用执行中也有创建合约,会执行CREATE操作符,也是这个之行流程// Contract creation by an external account is the same as CREATE opcodereturn createOpcode(_txSender, _endowment, _gasPrice, _gas, _init, _origin);
}bool Executive::createOpcode(Address const& _sender, u256 const& _endowment, u256 const& _gasPrice, u256 const& _gas, bytesConstRef _init, Address const& _origin)
{// 新合约账户的地址的生成u256 nonce = m_s.getNonce(_sender);m_newAddress = right160(sha3(rlpList(_sender, nonce)));return executeCreate(_sender, _endowment, _gasPrice, _gas, _init, _origin);
}bool Executive::executeCreate(Address const& _sender, u256 const& _endowment, u256 const& _gasPrice, u256 const& _gas, bytesConstRef _init, Address const& _origin)
{if (_sender != MaxAddress ||m_envInfo.number() < m_sealEngine.chainParams().experimentalForkBlock)  // EIP86m_s.incNonce(_sender);// 重新记录回滚点,可以得出nonce的增加是不可改变的m_savepoint = m_s.savepoint();m_isCreation = true;// We can allow for the reverted state (i.e. that with which m_ext is constructed) to contain the m_orig.address, since// we delete it explicitly if we decide we need to revert.m_gas = _gas;bool accountAlreadyExist = (m_s.addressHasCode(m_newAddress) || m_s.getNonce(m_newAddress) > 0);if (accountAlreadyExist){LOG(m_detailsLogger) << "Address already used: " << m_newAddress;m_gas = 0;m_excepted = TransactionException::AddressAlreadyUsed;revert();m_ext = {}; // cancel the _init execution if there are any scheduled.return !m_ext;}// Transfer ether before deploying the code. This will also create new// account if it does not exist yet.// 给新账户转账,这个内部执行中,判断是不存在的地址,会创建账户,再最后state commit的时候,写入到DB中m_s.transferBalance(_sender, m_newAddress, _endowment);u256 newNonce = m_s.requireAccountStartNonce();if (m_envInfo.number() >= m_sealEngine.chainParams().EIP158ForkBlock)newNonce += 1;// 增加noncem_s.setNonce(m_newAddress, newNonce);// 清理存储m_s.clearStorage(m_newAddress);// Schedule _init execution if not empty.// tx.data不为空,即有代码,这里初始化EVM实例if (!_init.empty())m_ext = make_shared<ExtVM>(m_s, m_envInfo, m_sealEngine, m_newAddress, _sender, _origin,_endowment, _gasPrice, bytesConstRef(), _init, sha3(_init), m_depth, true, false);return !m_ext;
}

这里返回false,表示后面要执行go(),

bool Executive::go(OnOpFunc const& _onOp)
{if (m_ext){try{// 创建VM的实例,VM类型有三种,缺省VMKind::DLL 还有可配置的VMKind::Interpreter VMKind::Legacy // Create VM instance. Force Interpreter if tracing requested.auto vm = VMFactory::create();if (m_isCreation){进入vm,执行合约创建auto out = vm->exec(m_gas, *m_ext, _onOp);if (m_res){m_res->gasForDeposit = m_gas;m_res->depositSize = out.size();}if (m_res)m_res->output = out.toVector(); // copy output to execution resultm_s.setCode(m_ext->myAddress, out.toVector());}else// 非合约创建,即消息调用,执行m_output = vm->exec(m_gas, *m_ext, _onOp);}}return true;
}// 最终进入这里
/// Handy wrapper for evmc_execute().
EVM::Result EVM::execute(ExtVMFace& _ext, int64_t gas)
{auto mode = toRevision(_ext.evmSchedule());evmc_call_kind kind = _ext.isCreate ? EVMC_CREATE : EVMC_CALL;uint32_t flags = _ext.staticCall ? EVMC_STATIC : 0;assert(flags != EVMC_STATIC || kind == EVMC_CALL);  // STATIC implies a CALL.evmc_message msg = {kind, flags, static_cast<int32_t>(_ext.depth), gas, toEvmC(_ext.myAddress),toEvmC(_ext.caller), _ext.data.data(), _ext.data.size(), toEvmC(_ext.value),toEvmC(0x0_cppui256)};return EVM::Result{evmc_execute(m_instance, &_ext, mode, &msg, _ext.code.data(), _ext.code.size())};
}

从上面的流程可以验证前面的一遍关于以太坊黄皮书理解的总结的,最红的交易会转成message(evmc_message)然后进入EVM computation,开始处理了。

消息调用

bool Executive::call(Address const& _receiveAddress, Address const& _senderAddress, u256 const& _value, u256 const& _gasPrice, bytesConstRef _data, u256 const& _gas)
{// 准备下参数,receiveAddress也是codeAddress,后面会根据这个地址取得codeCallParameters params{_senderAddress, _receiveAddress, _receiveAddress, _value, _value, _gas, _data, {}};return call(params, _gasPrice, _senderAddress);
}bool Executive::call(CallParameters const& _p, u256 const& _gasPrice, Address const& _origin)
{......m_gas = _p.gas;if (m_s.addressHasCode(_p.codeAddress)){//取得codebytes const& c = m_s.code(_p.codeAddress);//取得codehashh256 codeHash = m_s.codeHash(_p.codeAddress);m_ext = make_shared<ExtVM>(m_s, m_envInfo, m_sealEngine, _p.receiveAddress,_p.senderAddress, _origin, _p.apparentValue, _gasPrice, _p.data, &c, codeHash,m_depth, false, _p.staticCall);}......// Transfer ether.m_s.transferBalance(_p.senderAddress, _p.receiveAddress, _p.valueTransfer);return !m_ext;
}

代码也很好理解,后面就回到了上面的go()函数里面的else,还是转成message,进入EVM computation开始计算

小结
以太坊两种消息类型,分别进行处理,就像前面对黄皮书的总结,根据所处的环境上下文(区块好,区块gaslimit等等),原始的交易(from to code data等)构建message,然后由EVM进行执行

3.2 区块POW
这里就开始涉及到ethereum的POW过程了,再次之前要增加些其他的流程代码

3.2.1

POW的参与者分为 Farm 和 Miner,即农场和矿工,对应不同的接口

// A miner - a member and adoptee of the Farm.
template <class PoW> class GenericMiner
// Class for hosting one or more Miners.
template <class PoW> class GenericFarmFace//只有一个工作,就是提交工作量证明// _p 即找到的解决方案 struct Solution{Nonce nonce;h256 mixHash;};// _finder即方案的发现者virtual bool submitProof(Solution const& _p, Miner* _finder) = 0;// A collective of Miners. Miners ask for work, then submit proofs
template <class PoW> class GenericFarm: public GenericFarmFace<PoW>

3.2.2

POW的执行算法 Ethash,在 aleth\main.cpp中对Ethash进行了初始化

int main(int argc, char** argv)
{......Ethash::init();......
}void Ethash::init()
{ETH_REGISTER_SEAL_ENGINE(Ethash);
}#define ETH_REGISTER_SEAL_ENGINE(Name) static SealEngineFactory __eth_registerSealEngineFactory ## Name = SealEngineRegistrar::registerSealEngine<Name>(#Name)template <class SealEngine> static SealEngineFactory registerSealEngine(std::string const& _name) { return (get()->m_sealEngines[_name] = [](){return new SealEngine;}); }private://单例模式static SealEngineRegistrar* get() { if (!s_this) s_this = new SealEngineRegistrar; return s_this; }

最终是new了 Ethash 对象,即一个 SealEngine,最终放入到 SealEngineRegistrar 的 std::unordered_map<std::string, SealEngineFactory> m_sealEngines;
讲过上面步骤的一步一步执行,最终Ethash实例保存到了 SealEngineRegistrar的m_sealEngines中,名字就叫 Ethash

这里也要看下Ethash的构造过程

Ethash::Ethash()
{// 这里创建一个叫做cpu的矿工,又叫做sealermap<string, GenericFarm<EthashProofOfWork>::SealerDescriptor> sealers;sealers["cpu"] = GenericFarm<EthashProofOfWork>::SealerDescriptor{&EthashCPUMiner::instances, [](GenericMiner<EthashProofOfWork>::ConstructionInfo ci){ return new EthashCPUMiner(ci); }};// 把矿工加入到农场中m_farm.setSealers(sealers);// 为农场设置onSolutionFound 方法,传入Solution,进行检验m_farm.onSolutionFound([=](EthashProofOfWork::Solution const& sol){std::unique_lock<Mutex> l(m_submitLock);
//        cdebug << m_farm.work().seedHash << m_farm.work().headerHash << sol.nonce << EthashAux::eval(m_farm.work().seedHash, m_farm.work().headerHash, sol.nonce).value;setMixHash(m_sealing, sol.mixHash);setNonce(m_sealing, sol.nonce);// 对POW的检验if (!quickVerifySeal(m_sealing))return false;if (m_onSealGenerated){RLPStream ret;m_sealing.streamRLP(ret);l.unlock();m_onSealGenerated(ret.out());}return true;});
}

综上,Ethash里面有农场(GenericFarm<EthashProofOfWork>),农场里面有检验员,名字叫cpu,是一个 EthashCPUMine 实例, EthashCPUMiner 继承了 GenericMiner<EthashProofOfWork> 农场继承了 GenericFarmFace

怎么取用Ethash实例呢,通过名字

static SealEngineFace* create(std::string const& _name) { if (!get()->m_sealEngines.count(_name)) return nullptr; return get()->m_sealEngines[_name]();
}

BlockChain又是怎么来取用的呢?

1. 在struct ChainOperationParams 有三类,默认是 NoProof
    /// The chain sealer name: e.g. Ethash, NoProof, BasicAuthority (POA)
    std::string sealEngineName = "NoProof";
 
2. 在加载配置时,
ChainParams ChainParams::loadConfig 会确定用哪一种,例如名称为 Ethash (pow)
 
3. 区块链初始化时,会初始化自己的 m_sealEngine

void BlockChain::init(ChainParams const& _p)m_sealEngine.reset(m_params.createSealEngine());SealEngineFace* ChainParams::createSealEngine()
{SealEngineFace* ret = SealEngineRegistrar::create(sealEngineName);....
}

4. 至此,BlockChain 有了自己的检验引擎,即 m_sealEngine,又即 Ethash 实例
 客户端又怎么获取到BlockChain的Ethash?

SealEngineFace* sealEngine() const override { return bc().sealEngine(); } 这样就获得了上面的检验引擎
3.2.3

上面分析了POW检验者的建立过程,再接上最上面提到的最后一个步骤,开始挖矿

void Client::rejigSealing()
{if ((wouldSeal() || remoteActive()) && !isMajorSyncing()){// 这里实际就获得了Ethash的实例if (sealEngine()->shouldSeal(this)){......//这里的m_working就是上面执行完交易后的区块,下面的函数是封装区块内容// 由注释就可以知道,最后区块剩下没有完成的部分就是计算nonce mixhash了//Sealing/// Prepares the current state for mining./// Commits all transactions into the trie, compiles uncles and transactions list, applies all/// rewards and populates the current block header with the appropriate hashes./// The only thing left to do after this is to actually mine().///m_working.commitToSeal(bc(), m_extraData);......if (wouldSeal()){sealEngine()->onSealGenerated([=](bytes const& _header) {if (this->submitSealed(_header))m_onBlockSealed(_header);elseLOG(m_logger) << "Submitting block failed...";});// 开始挖矿sealEngine()->generateSeal(m_sealingInfo);}}......}......
}void Ethash::generateSeal(BlockHeader const& _bi)
{Guard l(m_submitLock);m_sealing = _bi;m_farm.setWork(m_sealing);m_farm.start(m_sealer);m_farm.setWork(m_sealing);
}

上面的流程注释了一些,构建了区块,剩下nonce mixhash,再最后调用Ethash开始挖矿计算,最终到了

void EthashCPUMiner::kickOff()void EthashCPUMiner::startWorking()
{if (!m_thread){m_shouldStop = false;m_thread.reset(new thread(&EthashCPUMiner::minerBody, this));}
}void EthashCPUMiner::minerBody()
{setThreadName("miner" + toString(index()));auto tid = std::this_thread::get_id();static std::mt19937_64 s_eng((utcTime() + std::hash<decltype(tid)>()(tid)));// 先计算一个nonce的随机起始值uint64_t tryNonce = s_eng();// FIXME: Use epoch number, not seed hash in the work package.WorkPackage w = work();// 根据seedHash找到现在的纪元 DAGint epoch = ethash::find_epoch_number(toEthash(w.seedHash));auto& ethashContext = ethash::get_global_epoch_context_full(epoch);// 获取现在的难度值 h256 boundary = w.boundary;// 开始穷举nonce,再DAG范围内寻找答案了,即POW的计算过程for (unsigned hashCount = 1; !m_shouldStop; tryNonce++, hashCount++){auto result = ethash::hash(ethashContext, toEthash(w.headerHash()), tryNonce);h256 value = h256(result.final_hash.bytes, h256::ConstructFromPointer);if (value <= boundary && submitProof(EthashProofOfWork::Solution{(h64)(u64)tryNonce,h256(result.mix_hash.bytes, h256::ConstructFromPointer)}))break;if (!(hashCount % 100))accumulateHashes(100);}
}

这里到了ethereum的核心 精华部分,POW的计算过程,根据协议的实现会有个难度值的计算,我在前面总结过了,这里就是穷举nonce,找到value使得它小于boundary,找到后,就提交工作量证明 submitProof

onSolutionFound ---> Ethash::quickVerifySeal

暂时代码梳理到这里,基本上算是一个交易的流程,后面深入学习下 Executive 以及 信息的同步过程等
————————————————

原文链接:https://blog.csdn.net/laorenmen/article/details/85478282

跟随一笔交易来看以太坊c++客户端源码执行流程 / 源码分析相关推荐

  1. 以太坊C++客户端Aleth源码分析,转账交易和智能合约的入口代码

    本文主要记录以太坊C++客户端Aleth的源码分析和相关实验过程和结果.本文将讲解两部分的内容,一是转账交易和智能合约的入口代码在哪里?二是通过实验验证转账交易和智能合约交易这两种不同交易所对应的不同 ...

  2. 区块链开发(十四)以太坊go-ethereum客户端查询交易列表探讨

    比特币是可以通过api(listtransactions)查询指定地址的历史交易的.但在eth中没有提供类似的查询api.Hyperledger fabric也有相应的查询历史交易记录的方法,利用如下 ...

  3. 以太坊go-ethereum客户端查询交易列表(二)

    玩过比特币的朋友都知道,比特币是可以通过api(listtransactions)查询指定地址的历史交易的.但在eth中没有提供类似的查询api.今天这篇博客就简单介绍一下如果解决这个问题. 问题 以 ...

  4. geth rpc无法开启_三分了解以太坊 Geth 客户端快照加速机制

    免责声明:本文旨在传递更多市场信息,不构成任何投资建议.文章仅代表作者观点,不代表火星财经官方立场. 小编:记得关注哦 来源:以太坊爱好者 原文标题:三分了解以太坊 Geth 客户端快照加速机制 原文 ...

  5. 通过命令行在Python中测试以太坊RPC客户端

    2019独角兽企业重金招聘Python工程师标准>>> 在这个笔记中,我将使用Python命令行测试以太坊的RPC客户端,准备好狂敲键盘吧.过程中有关JSON RPC的更多信息,可以 ...

  6. 以太坊智能合约之如何执行智能合约?

    区块链技术在顶级技术中占据主导地位的主要原因在于其去中心化.虽然区块链的主要目的是在没有中心的情况下维护交易记录,但为了实现自动化,智能合约被引入.那么在写完智能合约之后呢?在本文的这个以太坊智能合约 ...

  7. [币严区块链]数字货币交易所之以太坊(ETH)钱包对接(一) 以太坊Geth客户端安装...

    以太坊Geth客户端安装 geth是以太坊的官方客户端,它是一个命令行工具,提供很多命令和选项,可以运行以太坊节点.创建和管理账户.发送交易.挖矿.部署智能合约等. 下面介绍geth的三种安装方法: ...

  8. 区块链技术进阶-深入详解以太坊智能合约语言 solidity(含源码)-熊丽兵-专题视频课程...

    区块链技术进阶-深入详解以太坊智能合约语言 solidity(含源码)-103人已学习 课程介绍         区块链开发技术进阶-深入详解以太坊智能合约语言 solidity视频培训教程:本课程是 ...

  9. 以太坊不同客户端的定义和用途

    以太坊不同客户端的定义和用途  李赫 2016.07.03 以太坊发布以后,有 多个客户端,同时也支持多个平台,初学者往往被各种各样的客户端弄迷糊,本文进行了详细的总结. 更多文章请看http://b ...

最新文章

  1. 轻量级持久层V2版本代码与模板
  2. SCCM部署操作系统失败:MP has rejected a request
  3. linux安装python_VTK:华为笔记本电脑+深度deepin-linux+python下安装和入门
  4. python文件管理包_Python标准库04 文件管理 (部分os包,shutil包)
  5. python读取plt文件吗_用Python读取文件并绘制CDF
  6. 程序设计与算法----分治之归并排序
  7. 负载均衡器如何在服务器之间分配客户端流量?
  8. 如何实现用Delphi访问Outlook Express的收发邮件箱中的邮件?
  9. 使用redis作为缓存,数据还需要存入数据库中吗?
  10. web api authentication
  11. 安装Pure-ftpd
  12. Arduino应用开发——通过小爱同学控制灯光
  13. 王者荣耀微信登陆不了服务器,王者荣耀微信区怎么登陆不了 王者荣耀微信区怎么登不上...
  14. Unity | 基础逻辑
  15. 小程序 | 微信小程序实现星级评分与星级评分展示
  16. git did not exit cleanly(exit code 128)报错的部分原因及解决方法
  17. 一款全新的网页数据采集工具:爬山虎采集器
  18. STM32:红外传感器代码部分(内含实物图+外部信号流程,编写代码思路+代码+解析代码和扩展应用)
  19. C语言补习(第一周笔记)
  20. ez-usb fx3 linux,Mouser带来最新USB 3.0技术:赛普拉斯EZ-USB FX3和恩智浦USB 3.0超高速转接驱动器...

热门文章

  1. mysql和html的联系_前台与PHP与Mysql三者的联系
  2. 【c语言】蓝桥杯算法提高 JOE的算数
  3. BIOS不识别linux,linux – 在BIOS中启用VT但KVM无法检测到
  4. Oracle中INSTR和SUBSTR的用法
  5. LeetCode--046--全排列(java)
  6. Android-Toolbar相关
  7. https原理与实践
  8. Nginx允许跨域和禁止跨域操作
  9. linux iptables常用命令之配置生产环境iptables及优化
  10. 2016年云安全调查报告(更新版)