diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libblockchain.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libblockchain.md new file mode 100644 index 000000000..9c02d572c --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libblockchain.md @@ -0,0 +1,87 @@ +# libblockchain 模块 +作者:TrustChain [微信公众号] + +libblockchain:管理区块、交易、区块链状态等信息的读写。 + + + +## 主要内容有: + ++ 管理区块、交易、区块链状态等信息的读写。 + +![](../../../../images/articles/sourceCode_knowledge_map/libblockchain.png) + + +## 涉及知识点: ++ 区块缓存数据结构: +``` +mutable std::map> m_blockCache; +mutable std::deque m_blockCacheFIFO; // insert queue log for m_blockCache +``` ++ getBlockHeaderInfoByHash:先查缓存m_blockCache,查不到再查询系统表; + ++ SYS_HASH_2_BLOCKHEADER,如果查询不到,再getBlock函数获取区块; + ++ getBlock函数:先查缓存m_blockCache,查不到再查询系统表SYS_HASH_2_BLOCK; + ++ getBlockRLP函数:访问SYS_NUMBER_2_HASH、SYS_HASH_2_BLOCK获取区块的RLP编码数据; + ++ initGenesisWorkingSealers:用于共识算法rpbft,初始化共识节点队列,在sealerList随机选择共识节点workingSealers; + ++ commitBlock函数 +``` +tbb::parallel_invoke([this, block, context]() { writeHash2Block(*block, context); }, + [this, block, context]() { writeNumber2Hash(*block, context); }, + [this, block, context]() { writeNumber(*block, context); }, + [this, block, context]() { writeTotalTransactionCount(*block, context); }, + [this, block, context]() { writeTxToBlock(*block, context); }, + [this, block, context]() { writeHash2BlockHeader(*block, context); }); +``` ++ 计算区块的交易或者回执的默克尔根 +``` + +std::pair> m_receiptChild2ParentCache; + std::pair> m_txsChild2ParentCache; +//获取默克尔根 +void BlockChainImp::getMerkleProof(dev::bytes const& _txHash, + const std::map>& parent2ChildList, + const Child2ParentMap& child2Parent, + std::vector, std::vector>>& merkleProof) +{ + std::string merkleNode = toHex(_txHash); + //得到merkleNode的自身节点itChild2Parent,设为A + auto itChild2Parent = child2Parent.find(merkleNode); + while (itChild2Parent != child2Parent.end()) + { + //itChild2Parent->second,即A的父节点B,itParent2ChildList= + auto itParent2ChildList = parent2ChildList.find(itChild2Parent->second); + if (itParent2ChildList == parent2ChildList.end()) + { + break; + } + // get index from itParent2ChildList->second by merkleNode + //,itChildlist= merkleNode + auto itChildlist = std::find( + itParent2ChildList->second.begin(), itParent2ChildList->second.end(), merkleNode); + if (itChildlist == itParent2ChildList->second.end()) + { + break; + } + // copy to merkle proof path + std::vector leftpath; + std::vector rightpath; + leftpath.insert(leftpath.end(), itParent2ChildList->second.begin(), itChildlist); + rightpath.insert(rightpath.end(), std::next(itChildlist), itParent2ChildList->second.end()); + merkleProof.push_back(std::make_pair(std::move(leftpath), std::move(rightpath))); + //itChild2Parent->second,即A的父节点B,merkleNode指向父节点B + merkleNode = itChild2Parent->second; + //从下往上计算默克尔树根 + itChild2Parent = child2Parent.find(merkleNode); + } +} +``` +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libblockverifier.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libblockverifier.md new file mode 100644 index 000000000..22933748b --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libblockverifier.md @@ -0,0 +1,314 @@ +# libblockverifier 模块 + +作者:TrustChain [微信公众号] + +libblockverifier:一个区块的执行逻辑,包括DAG并行实现。 + + + +## 主要内容有: + ++ 数据结构:顶点(Vertex)、DAG、TxDAG。 + ++ 区块的串行执行、并行执行等。 + + +![](../../../../images/articles/sourceCode_knowledge_map/libblockverifier.png) + +## 涉及知识点: + ++ DAG.cpp +``` +//顶点数据结构 +struct Vertex +{ + std::atomic inDegree; + std::vector outEdge; +}; +//DAG数据结构 +class DAG +{ +private: + std::vector> m_vtxs; + //初始化时,先存放入度为0的顶点,接着会存放入度为1的顶点等 + tbb::concurrent_queue m_topLevel; + ID m_totalVtxs = 0; + std::atomic m_totalConsume; +} +//为两个顶点添加一条边 +void DAG::addEdge(ID _f, ID _t) +{ + if (_f >= m_vtxs.size() && _t >= m_vtxs.size()) + return; + m_vtxs[_f]->outEdge.emplace_back(_t); + //边的终点的入度+1 + m_vtxs[_t]->inDegree += 1; +} +//从入度为0的_id出发进行消费与_id相接的顶点集合 +ID DAG::consume(ID _id) +{ + ID producedNum = 0; + ID nextId = INVALID_ID; + ID lastDegree = INVALID_ID; + for (ID id : m_vtxs[_id]->outEdge) + { + //获取与_id相接的顶点vtx + auto vtx = m_vtxs[id]; + { + //vtx入度-1 + lastDegree = vtx->inDegree.fetch_sub(1); + } + //顶点vtx还有一条边的情况 + if (lastDegree == 1) + { + ++producedNum; + //记录第一次遇到的下个待处理的顶点nextId + if (producedNum == 1) + { + nextId = id; + } + else + { + //将入度为1的顶点入队列 + m_topLevel.push(id); + cv_topLevel.notify_one(); + } + } + } + + if (m_totalConsume.fetch_add(1) + 1 == m_totalVtxs) + { + cv_topLevel.notify_all(); + } + return nextId; +} +``` + ++ TxDAG.cpp +``` +/**重要的数据结构 +//临界域 +template +class CriticalField +{ +private: + std::map m_criticals; +} + +using ExecuteTxFunc = + std::function; + +class TxDAG : public TxDAGFace +{ +private: + ExecuteTxFunc f_executeTx; + std::shared_ptr m_txs; + DAG m_dag; + ID m_exeCnt = 0; + ID m_totalParaTxs = 0; + mutable std::mutex x_exeCnt; +} +*/ + +//可并行执行的单元 +int TxDAG::executeUnit(Executive::Ptr _executive) +{ + int exeCnt = 0; + ID id = m_dag.waitPop(); + while (id != INVALID_ID) + { + do + { + exeCnt += 1; + f_executeTx((*m_txs)[id], id, _executive); + //主要是减边操作 + id = m_dag.consume(id); + } while (id != INVALID_ID); + //从队列 m_topLevel取出下一个待处理的节点 + id = m_dag.waitPop(); + } + if (exeCnt > 0) + { + Guard l(x_exeCnt); + m_exeCnt += exeCnt; + } + return exeCnt; +} +``` + ++ ExecutiveContext.cpp +``` +//调用对应预编译合约的call函数,并返回结果execResult +dev::precompiled::PrecompiledExecResult::Ptr ExecutiveContext::call( + Address const& address, bytesConstRef param, Address const& origin, Address const& sender) +{ +auto p = getPrecompiled(address); +if (p) +{ + auto execResult = p->call(shared_from_this(), param, origin, sender); + return execResult; +} +} + +//注册预编译合约,插入m_address2Precompiled +Address ExecutiveContext::registerPrecompiled(std::shared_ptr p) +{ + auto count = ++m_addressCount; + Address address(count); + if (!p->precompiledExecResultFactory()) + { + p->setPrecompiledExecResultFactory(m_precompiledExecResultFactory); + } + m_address2Precompiled.insert(std::make_pair(address, p)); + + return address; +} + +//执行预编译合约 +std::pair ExecutiveContext::executeOriginPrecompiled( + Address const& _a, bytesConstRef _in) const +{ + return m_precompiledContract.at(_a).execute(_in); +} +//得到临界域的值:用户账户 +std::shared_ptr> ExecutiveContext::getTxCriticals(const Transaction& _tx) +{ +auto p = getPrecompiled(_tx.receiveAddress()); +if (p) +{ + // Precompile transaction + if (p->isParallelPrecompiled()) + { + //获取交易中的用户账号,并存入vector + auto ret = make_shared>(p->getParallelTag(ref(_tx.data()))); + for (string& critical : *ret) + { + critical += _tx.receiveAddress().hex(); + } + return ret; + } + else + { + return nullptr; + } +} +else +{ +//要嘛从缓存或者配置中获取 +} +} +``` + ++ BlockVerifier.cpp +``` +//串行执行区块逻辑 +ExecutiveContext::Ptr BlockVerifier::serialExecuteBlock( + Block& block, BlockInfo const& parentBlockInfo) +{ +//第一步:初始化executiveContext +ExecutiveContext::Ptr executiveContext = std::make_shared(); +m_executiveContextFactory->initExecutiveContext( + parentBlockInfo, parentBlockInfo.stateRoot, executiveContext); + +//第二步:执行区块的交易并回执 +EnvInfo envInfo(block.blockHeader(), m_pNumberHash, 0); +envInfo.setPrecompiledEngine(executiveContext); +auto executive = createAndInitExecutive(executiveContext->getState(), envInfo); +for (size_t i = 0; i < block.transactions()->size(); i++) +{ + auto& tx = (*block.transactions())[i]; + TransactionReceipt::Ptr resultReceipt = execute(tx, executiveContext, executive); + block.setTransactionReceipt(i, resultReceipt); + executiveContext->getState()->commit(); +} +//第三步:设置状态根 +h256 stateRoot = executiveContext->getState()->rootHash(); +block.header().setStateRoot(stateRoot); +block.header().setDBhash(stateRoot); +block.header().setDBhash(executiveContext->getMemoryTableFactory()->hash()); +} +``` + +``` +//并行执行区块 +ExecutiveContext::Ptr BlockVerifier::parallelExecuteBlock( + Block& block, BlockInfo const& parentBlockInfo) +{ +//第一步:初始化executiveContext、 +ExecutiveContext::Ptr executiveContext = std::make_shared(); +m_executiveContextFactory->initExecutiveContext( +parentBlockInfo, parentBlockInfo.stateRoot, executiveContext); + +//第二步:初始化交易的txDag,并设置每个交易的执行函数 +shared_ptr txDag = make_shared(); +txDag->init(executiveContext, block.transactions(), block.blockHeader().number()); +txDag->setTxExecuteFunc([&](Transaction::Ptr _tr, ID _txId, Executive::Ptr _executive) { + auto resultReceipt = execute(_tr, executiveContext, _executive); + block.setTransactionReceipt(_txId, resultReceipt); + executiveContext->getState()->commit(); + return true; +}); + +//第三步:并行执行交易 +try +{ +tbb::atomic isWarnedTimeout(false); +tbb::parallel_for(tbb::blocked_range(0, m_threadNum), + [&](const tbb::blocked_range& _r) { + (void)_r; + EnvInfo envInfo(block.blockHeader(), m_pNumberHash, 0); + envInfo.setPrecompiledEngine(executiveContext); + auto executive = createAndInitExecutive(executiveContext->getState(), envInfo); + + while (!txDag->hasFinished()) + { + if (!isWarnedTimeout.load() && utcSteadyTime() >= parallelTimeOut) + { + isWarnedTimeout.store(true); } + + txDag->executeUnit(executive); + } + }); +} +catch (exception& e) +{ + +} + +//第四步:设置状态根 +h256 stateRoot = executiveContext->getState()->rootHash(); +block.header().setStateRoot(stateRoot); +block.header().setDBhash(stateRoot); +block.header().setDBhash(executiveContext->getMemoryTableFactory()->hash()); + +} +``` + +``` +TransactionReceipt::Ptr BlockVerifier::executeTransaction( + const BlockHeader& blockHeader, dev::eth::Transaction::Ptr _t) +{ +ExecutiveContext::Ptr executiveContext = std::make_shared(); +BlockInfo blockInfo{blockHeader.hash(), blockHeader.number(), blockHeader.stateRoot()}; +try +{ + m_executiveContextFactory->initExecutiveContext( + blockInfo, blockHeader.stateRoot(), executiveContext); +} +catch (exception& e) +{ + +} +EnvInfo envInfo(blockHeader, m_pNumberHash, 0); +envInfo.setPrecompiledEngine(executiveContext); +auto executive = createAndInitExecutive(executiveContext->getState(), envInfo); +// only Rpc::call will use executeTransaction, RPC do catch exception +return execute(_t, executiveContext, executive); +} +``` + +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ \ No newline at end of file diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libchannelserver.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libchannelserver.md new file mode 100644 index 000000000..bce8b6b78 --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libchannelserver.md @@ -0,0 +1,242 @@ +# libchannelserver 模块 +作者:TrustChain [微信公众号] + +libchannelserver:交互式控制台和客户端SDK连接Channel端口。 + +## 主要内容有: + ++ 交互式控制台和客户端SDK连接Channel端口:证书认证、TCP长连接、支持AMOP协议的点对点通信等; + ++ ChannelMessage格式:节点与SDK间通信的数据包为ChannelMessage格式; + +![](../../../../images/articles/sourceCode_knowledge_map/libchannelserver.png) + +## 涉及知识点: ++ ChannelMessage.h + +``` +//ChannelMessage消息类型 +enum ChannelMessageType +{ + CHANNEL_RPC_REQUEST = 0x12, // type for rpc request + CLIENT_HEARTBEAT = 0x13, // type for heart beat for sdk + CLIENT_HANDSHAKE = 0x14, // type for hand shake + CLIENT_REGISTER_EVENT_LOG = 0x15, // type for event log filter register request and response + CLIENT_UNREGISTER_EVENT_LOG = 0x16, // type for event log filter unregister request and + // response + AMOP_REQUEST = 0x30, // type for request from sdk + AMOP_RESPONSE = 0x31, // type for response to sdk + AMOP_CLIENT_SUBSCRIBE_TOPICS = 0x32, // type for topic request + AMOP_MULBROADCAST = 0x35, // type for mult broadcast + REQUEST_TOPICCERT = 0x37, // type request verify + UPDATE_TOPIICSTATUS = 0x38, // type for update status + TRANSACTION_NOTIFY = 0x1000, // type for transaction notify + BLOCK_NOTIFY = 0x1001, // type for block notify + EVENT_LOG_PUSH = 0x1002 // type for event log push +}; + +/**ChannelMessage格式 +m_length 4B +m_type 2B +m_seq 32B +m_result 2B +m_data 100MB +*/ +const static size_t MIN_HEADER_LENGTH = 4; +const static size_t HEADER_LENGTH = 4 + 2 + 32 + 4; +const static size_t MAX_LENGTH = 100 * 1024 * 1024; // max 100MB + +//TopicChannelMessage函数,用户用于topic消息的pub-sub订阅模式点对点通信,格式为ChannelMessage格式。 +//TopicVerifyChannelMessage函数,用户修改TopicChannelMessage内容 +``` + ++ ChannelRPCServer.cpp +``` +bool ChannelRPCServer::StartListening() +{ +if (!_running) +{ +CHANNEL_LOG(TRACE) << "Start ChannelRPCServer" << LOG_KV("Host", _listenAddr) << ":"<< _listenPort; +//节点监听channel_listen_port,负责处理客户端ChannelMessage请求 +//dev::channel::ChannelSession +//ChannelRPCServer::onConnect +std::function fp = + std::bind(&ChannelRPCServer::onConnect, shared_from_this(), std::placeholders::_1, std::placeholders::_2); +_server->setConnectionHandler(fp); +_server->run(); + +//节点与其他节点AMOP类型的P2PMessage点对点通信 +//dev::p2p::P2PSession +//ChannelRPCServer::onNodeChannelRequest +//dev::eth::ProtocolID::AMOP + +std::function,p2p::P2PMessage::Ptr)> +channelHandler = std::bind(&ChannelRPCServer::onNodeChannelRequest, shared_from_this(),std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); +m_service->registerHandlerByProtoclID(dev::eth::ProtocolID::AMOP, channelHandler); +} + +void ChannelRPCServer::onConnect( + dev::channel::ChannelException e, dev::channel::ChannelSession::Ptr session) +{ +std::function +fp = std::bind(&dev::ChannelRPCServer::onClientRequest, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); +session->setMessageHandler(fp); +} +``` + +``` +//节点处理与客户端RPC连接的ChannelMessage请求 +void dev::ChannelRPCServer::onClientRequest(dev::channel::ChannelSession::Ptr session, +dev::channel::ChannelException e, dev::channel::Message::Ptr message) +{ +if (e.errorCode() == 0) +{ + CHANNEL_LOG(TRACE) << "receive sdk message" << LOG_KV("length", message->length()) + << LOG_KV("type", message->type()) + << LOG_KV("seq", message->seq().substr(0, c_seqAbridgedLen)); + + switch (message->type()) + { + case CHANNEL_RPC_REQUEST: + onClientRPCRequest(session, message); + break; + case CLIENT_HEARTBEAT: + onClientHeartbeat(session, message); + break; + case CLIENT_HANDSHAKE: + onClientHandshake(session, message); + break; + case CLIENT_REGISTER_EVENT_LOG: + onClientRegisterEventLogRequest(session, message); + break; + case CLIENT_UNREGISTER_EVENT_LOG: + onClientUnregisterEventLogRequest(session, message); + break; + case AMOP_REQUEST: + case AMOP_RESPONSE: + case AMOP_MULBROADCAST: + onClientChannelRequest(session, message); + break; + case AMOP_CLIENT_SUBSCRIBE_TOPICS: + onClientTopicRequest(session, message); + break; + case UPDATE_TOPIICSTATUS: + onClientUpdateTopicStatusRequest(message); + break; + default: + CHANNEL_LOG(ERROR) << "unknown client message" << LOG_KV("type", message->type()); + break; + } +} +else +{ + CHANNEL_LOG(WARNING) << "onClientRequest" << LOG_KV("errorCode", e.errorCode()) + << LOG_KV("what", e.what()); + + onDisconnect(dev::channel::ChannelException(), session); +} +} + +``` + +``` +//节点与其他节点AMOP类型的P2PMessage点对点通信:涉及onNodeChannelRequest、onClientChannelRequest +//Node request, Node <--> Node AMOPMessage +void dev::ChannelRPCServer::onNodeChannelRequest( + dev::network::NetworkException, std::shared_ptr s, p2p::P2PMessage::Ptr msg) +{ +//channelMessage->type() == AMOP_REQUEST +asyncPushChannelMessage(topic, channelMessage, +[nodeID, channelMessage, service, p2pMessage](dev::channel::ChannelException e, +dev::channel::Message::Ptr response, dev::channel::ChannelSession::Ptr) { +} + +//channelMessage->type() == AMOP_MULBROADCAST +asyncBroadcastChannelMessage(topic, channelMessage); +} +//SDK channel2 request, sdk --> Node AMOPMessage +void dev::ChannelRPCServer::onClientChannelRequest( + dev::channel::ChannelSession::Ptr session, dev::channel::Message::Ptr message) +{ +//channelMessage->type() == AMOP_REQUEST +m_service->asyncSendMessageByTopic( +topic, p2pMessage, +[session, message](dev::network::NetworkException e, +std::shared_ptr, dev::p2p::P2PMessage::Ptr response) { +} +//message->type() == AMOP_MULBROADCAST +bool sended = m_service->asyncMulticastMessageByTopic( + topic, p2pMessage, m_networkBandwidthLimiter); + +} +``` + ++ ChannelServer.cpp + +``` +void dev::channel::ChannelServer::run() +{ +m_requestThreadPool = std::make_shared("ChannelReq", 16); +m_responseThreadPool = std::make_shared("ChannelResp", 8); +m_serverThread = std::make_shared([=]() { +startAccept(); +m_ioService->run(); +} +} + +void dev::channel::ChannelServer::startAccept() +{ +//调用ChannelServer::onAccept +m_acceptor->async_accept(session->sslSocket()->lowest_layer(), +boost::bind(&ChannelServer::onAccept, shared_from_this(), + boost::asio::placeholders::error, session)); + +} + +void dev::channel::ChannelServer::onAccept( + const boost::system::error_code& error, ChannelSession::Ptr session) +{ +session->sslSocket()->async_handshake(boost::asio::ssl::stream_base::server, +boost::bind(&ChannelServer::onHandshake, shared_from_this(), + boost::asio::placeholders::error, sdkPublicKey, session)); + m_connectionHandler(ChannelException(), session); +startAccept(); +} + +``` + ++ ChannelSession.cpp +``` +//发送消息--写操作 +//sendMessage()--》asyncSendMessage()--》writeBuffer()-->startWrite()-->onWrite() +``` + +``` +//接收消息--读操作 +//run()--》startRead()--》onRead()-->onMessage() + +void ChannelSession::onMessage(ChannelException e, Message::Ptr message) +{ +//情况1:处理返回消息 +m_responseThreadPool->enqueue([=]() { +response_callback->callback(e, message); +eraseResponseCallbackBySeq(message->seq()); +}); +//情况2:处理请求消息 +m_requestThreadPool->enqueue([session, message]() { +auto s = session.lock(); +if (s && s->_messageHandler) +{ + s->_messageHandler(s, ChannelException(0, ""), message); +} +}); +} +``` + +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ \ No newline at end of file diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libconfig.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libconfig.md new file mode 100644 index 000000000..449bd9319 --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libconfig.md @@ -0,0 +1,48 @@ +# libconfig 模块 +作者:TrustChain [微信公众号] + +libconfig:配置文件。 + +## 主要内容有: + ++ 网络固定配置文件config.genesis; + ++ 网络可改配置文件config.ini; + ++ 群组配置文件:群组固定配置文件group.N.genesis、群组可改配置文件group.N.ini; + +![](../../../../images/articles/sourceCode_knowledge_map/libconfig.png) + +## 涉及知识点: ++ 单例模式 + +1. 饿汉模式:由于要进行线程同步,所以在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。libconfig源码里使用的是此模式; +``` +class GlobalConfigure +{ +public: + static GlobalConfigure& instance() + { + static GlobalConfigure ins; + return ins; + } +} +``` +2.懒汉模式:在访问量较小时,采用懒汉实现。这是以时间换空间。 +``` + +class GlobalConfigure { +private: + static GlobalConfigure* m_instance; + GlobalConfigure(){} +public: + static GlobalConfigure* getInstance(); +} +``` + +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ + diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libconsensus.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libconsensus.md new file mode 100644 index 000000000..bda58ab22 --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libconsensus.md @@ -0,0 +1,103 @@ +# libconsensus 模块 + +作者:TrustChain [微信公众号] + +libconsensus:实现区块链的共识机制。 + +## 主要内容有: + ++ 插件化共识框架,Sealer封装打包流程,ConsensusEngineBase封装共识框架; + ++ Sealer线程:RaftSealer、PbftSealer、rPbftSealer; + ++ Engine线程:RaftEngine、PbftEngine、rPbftEngine。 + ++ 容错类算法CFT:当系统出现网络、磁盘故障,服务器宕机等普通故障时,仍能针对某个提议达成共识;这类算法性能较好、处理速度较快、可以容忍不超过一半的故障节点;不能防止节点作恶,可达到一致性。如Paxos算法、Raft算法。 + ++ 拜占庭容错类算法BFT:除了容忍系统共识过程中出现的普通故障外,还可容忍部分节点故意欺骗(如伪造交易执行结果)等拜占庭错误;这类算法性能较差,能容忍不超过三分之一的故障节点和作恶节点,可达到最终一致性。如:PBFT算法、rPBFT算法。 + +![](../../../../images/articles/sourceCode_knowledge_map/libconsensus.png) + +## 涉及知识点: ++ Sealer.cpp + +``` +//重要数据结构 +/// transaction pool handler +std::shared_ptr m_txPool; +/// handler of the block-sync module +std::shared_ptr m_blockSync; +/// handler of the block chain module +std::shared_ptr m_blockChain; +std::shared_ptr m_consensusEngine; +/// current sealing block(include block, transaction set of block and execute context) +Sealing m_sealing; + +/// start the Sealer module +void Sealer::start() +{ + if (m_startConsensus) + { + return; + } + //reset the sealing block before loadTransactions + resetSealingBlock(); + m_consensusEngine->reportBlock(*(m_blockChain->getBlockByNumber(m_blockChain->number()))); + m_maxBlockCanSeal = m_consensusEngine->maxBlockTransactions(); + m_syncBlock = false; + /// start a thread to execute doWork()&&workLoop() + startWorking(); + m_startConsensus = true; +} + +void Sealer::doWork(bool wait) +{ +loadTransactions(); +handleBlock(); +} + +/** + * @brief: load transactions from the transaction pool + * @param transToFetch: max transactions to fetch + */ +void Sealer::loadTransactions(uint64_t const& transToFetch) +{ + /// fetch transactions and update m_transactionSet + m_sealing.block->appendTransactions( + m_txPool->topTransactions(transToFetch, m_sealing.m_transactionSet, true)); +} + +``` + ++ ConsensusEngineBase.cpp +``` +/**涉及的主要数据结构 +/// p2p service handler +std::shared_ptr m_service; +/// transaction pool handler +std::shared_ptr m_txPool; +/// handler of the block chain module +std::shared_ptr m_blockChain; +/// handler of the block-sync module +std::shared_ptr m_blockSync; +/// handler of the block-verifier module +std::shared_ptr m_blockVerifier; +*/ +/// update m_sealing and receiptRoot +dev::blockverifier::ExecutiveContext::Ptr ConsensusEngineBase::executeBlock(Block& block) +{ + auto parentBlock = m_blockChain->getBlockByNumber(m_blockChain->number()); + BlockInfo parentBlockInfo{parentBlock->header().hash(), parentBlock->header().number(), + parentBlock->header().stateRoot()}; + /// reset execute context + return m_blockVerifier->executeBlock(block, parentBlockInfo); +} +``` + +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ + + diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libconsensus_pbft.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libconsensus_pbft.md new file mode 100644 index 000000000..494973bfa --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libconsensus_pbft.md @@ -0,0 +1,844 @@ +# libconsensus_pbft 子模块 + +作者:TrustChain [微信公众号] + +libconsensus_pbft:实现PBFT算法。 + +## 主要内容有: + ++ 采用签名、签名验证、哈希等密码学算法确保消息传递过程中的防篡改性、防伪造性、不可抵赖性;在一个由(3*f+1)个节点构成的系统中,只要有不少于(2*f+1)个非恶意节点正常工作,f为作恶节点数;CP强一致性算法。 + ++ 节点类型:共识节点(Leader节点和Replica节点)及Observer节点; + ++ 节点ID和节点索引:公私秘钥对、签名和验签等; + ++ 视图:leader每一轮共识都会切换;若leader为拜占庭节点,则会发送视图切换,其他共识节点会触发重新选择leader。若收到的区块为空块,也会发生视图切换; + ++ 共识消息:PrepareReq(包含区块的请求包)、SignReq(带有区块执行结果的签名请求)、CommitReq(用于确认区块执行结果的提交请求)和ViewChangeReq(视图切换请求); + ++ 核心流程:客户端向主节点发生请求、Pre-prepare(区块执行|签名区块|广播区块)、Prepare(收集签名包|广播commit包)、Commit(收集Commit包|缓存的区块上链)、主节点reply; + ++ 优化机制:消息转发机制:PBFT消息包中加入了forwardNodes字段记录了可达节点信息;优化Prepare包的结构:区块仅包含交易哈希。 + +![](../../../../images/articles/sourceCode_knowledge_map/libconsensus_pbft.png) + +## 涉及知识点: ++ Common.h + +``` +// for bip152: packetType for partiallyBlock +enum P2PPacketType : uint32_t +{ + // PartiallyPreparePacket + // note: the forwarded prepare messages include all the transaction data + PartiallyPreparePacket = 0x1, + // represent that the node should response the missed transaction data + GetMissedTxsPacket = 0x2, + // represent that the node receives the missed transaction data + MissedTxsPacket = 0x3, + // status of RawPrepareReq + P2PRawPrepareStatusPacket = 0x4, + // represent that the node should response the RawPrepareReq to the requested node + RequestRawPreparePacket = 0x5, + RawPrepareResponse = 0x6, +}; + +// for pbft +enum PBFTPacketType : byte +{ + PrepareReqPacket = 0x00, + SignReqPacket = 0x01, + CommitReqPacket = 0x02, + ViewChangeReqPacket = 0x03, + PBFTPacketCount +}; + +/// PBFT message +struct PBFTMsgPacket +{ +/// the index of the node that sends this pbft message +IDXTYPE node_idx; +/// the node id of the node that sends this pbft message +h512 node_id; +/// type of the packet(maybe prepare, sign or commit) +/// (receive from the network or send to the network) +byte packet_id; +/// ttl, extend the 8th bit to represent the inner prepareReq with empty block or not +mutable uint8_t ttl; +bool prepareWithEmptyBlock = false; +/// the data of concrete request(receive from or send to the network) +bytes data; +/// timestamp of receive this pbft message +u256 timestamp; +/// endpoint +std::string endpoint; +// the node that disconnected from this node, but the packet should reach +std::shared_ptr forwardNodes; + +} + +/// the base class of PBFT message +struct PBFTMsg +{ +} +``` + ++ PBFTReqCache.cpp + +``` +/**PBFTReqCache.h 缓存PBFTReq请求消息 +/// cache for prepare request +PrepareReq::Ptr m_prepareCache; +/// cache for raw prepare request +PrepareReq::Ptr m_rawPrepareCache; +/// cache for signReq(maps between hash and sign requests) +std::unordered_map> m_signCache; + +/// cache for received-viewChange requests(maps between view and view change requests) +std::unordered_map> + m_recvViewChangeReq; +// only record the latest view of all the nodes +std::shared_ptr> m_latestViewChangeReqCache; + +/// cache for committed requests(maps between hash and committed requests) +std::unordered_map> m_commitCache; +/// cache for prepare request need to be backup and saved +PrepareReq m_committedPrepareCache; +mutable SharedMutex x_committedPrepareCache; +/// cache for the future prepare cache +/// key: block hash, value: the cached future prepeare +std::unordered_map> m_futurePrepareCache; +const unsigned m_maxFuturePrepareCacheSize = 10; + +mutable SharedMutex x_rawPrepareCache; + +std::function m_randomSendRawPrepareStatusCallback; +std::function m_checkSignCallback; +*/ +``` + ++ PBFTMsgCache.h + +``` +/** +struct PBFTMsgCache +{ +private: +/// mutex for m_knownPrepare +mutable SharedMutex x_knownPrepare; +/// cache for the prepare packet +QueueSet m_knownPrepare; +/// mutex for m_knownSign +mutable SharedMutex x_knownSign; +/// cache for the sign packet +QueueSet m_knownSign; +/// mutex for m_knownCommit +mutable SharedMutex x_knownCommit; +/// cache for the commit packet +QueueSet m_knownCommit; +/// mutex for m_knownViewChange +mutable SharedMutex x_knownViewChange; +/// cache for the viewchange packet +QueueSet m_knownViewChange; + +/// the limit size for prepare packet cache +static const unsigned c_knownPrepare = 1024; +/// the limit size for sign packet cache +static const unsigned c_knownSign = 1024; +/// the limit size for commit packet cache +static const unsigned c_knownCommit = 1024; +/// the limit size for viewchange packet cache +static const unsigned c_knownViewChange = 1024; +} +*/ +//class PBFTBroadcastCache 的私有变量,主要存储最近节点广播各种消息的key +/// maps between node id and its broadcast cache +std::unordered_map> m_broadCastKeyCache; +``` + ++ PartiallyPBFTReqCache + +``` +/**class PartiallyPBFTReqCache 的私有变量,主要负责处理bip 152改进;可以从其他节点取回本节点缺失的交易集等 +PrepareReq::Ptr m_partiallyRawPrepare; +// add the prepareReq into m_preRawPrepare once leader generate the prepareReq +// this cache is used to response txs to the request-sealers after generate prepareReq while +// before addRawPrepareReq clear this cache when addRawPrepare +PrepareReq::Ptr m_preRawPrepare; +mutable SharedMutex x_preRawPrepare; +PrepareReq::Ptr m_partiallyFuturePrepare; +*/ +``` + ++ PBFTSealer.cpp: leader节点打包区块; + +``` +/**主要的参数值 +/// the minimum number of transactions that caused timeout;允许超时的交易最小数量,提高此参数值,则会increase maxBlockCanSeal +uint64_t m_lastTimeoutTx = 0; +/// the maximum number of transactions that has been consensused without timeout +uint64_t m_maxNoTimeoutTx = 0; +/// timeout counter +int64_t m_timeoutCount = 0; +std::shared_ptr m_pbftEngine; +*/ +// +void PBFTSealer::start() +{ + if (m_enableDynamicBlockSize) + { + /// onTimeout():decrease maxBlockCanSeal to half when timeout + m_pbftEngine->onTimeout(boost::bind(&PBFTSealer::onTimeout, this, boost::placeholders::_1)); +///onCommitBlock(): increase maxBlockCanSeal when commitBlock with no-timeout + m_pbftEngine->onCommitBlock(boost::bind(&PBFTSealer::onCommitBlock, this, + boost::placeholders::_1, boost::placeholders::_2, boost::placeholders::_3)); + m_lastBlockNumber = m_blockChain->number(); + } + //启动共识引擎服务 + m_pbftEngine->start(); + //调用父类的start()函数 + Sealer::start(); +} + +/** + * @brief: this node can generate block or not + * @return true: this node can generate block + * @return false: this node can't generate block + */ +bool PBFTSealer::shouldSeal() +{ + return Sealer::shouldSeal() && m_pbftEngine->shouldSeal(); +} + +//打包区块 +void PBFTSealer::handleBlock() +{ +resetSealingBlock(); +setBlock(); +/// sealing the generated block into prepareReq and push its to msgQueue +m_pbftEngine->generatePrepare(m_sealing.block); + +} +``` + ++ PBFTEngine.cpp +``` +/**重要数据结构 +//m_reqCache根据参数enablePrepareWithTxsHash初始化不同的缓存类型:PartiallyPBFTReqCache、PBFTReqCache; +std::shared_ptr m_reqCache; +std::shared_ptr m_broadCastCache; +TimeManager m_timeManager; +PBFTMsgQueue m_msgQueue; + +// bip 152 related logic:只传交易哈希值的包,减少带宽 +PartiallyPBFTReqCache::Ptr m_partiallyPrepareCache = nullptr; +std::shared_ptr>> m_cachedForwardMsg; +dev::ThreadPool::Ptr m_prepareWorker; +dev::ThreadPool::Ptr m_messageHandler; + +*/ + +void PBFTEngine::start() +{ + // create PBFTMsgFactory + createPBFTMsgFactory(); + //初始化m_reqCache + createPBFTReqCache(); + assert(m_reqCache); + // PBFTEngine::checkSign():set checkSignCallback for reqCache + m_reqCache->setCheckSignCallback(boost::bind(&PBFTEngine::checkSign, this, boost::placeholders::_1)); + + // PBFTEngine::handleP2PMessage():register P2P callback after create PBFTMsgFactory + m_service->registerHandlerByProtoclID( + m_protocolId, boost::bind(&PBFTEngine::handleP2PMessage, this, boost::placeholders::_1, boost::placeholders::_2, boost::placeholders::_3)); + registerDisconnectHandler(); + //调用父类的start() + ConsensusEngineBase::start(); + initPBFTEnv(3 * getEmptyBlockGenTime()); + PBFTENGINE_LOG(INFO) << "[Start PBFTEngine...]"; +} +/** + PBFTMsg(KeyPair const& _keyPair, int64_t const& _height, VIEWTYPE const& _view, + IDXTYPE const& _idx, h256 const _blockHash) + { + height = _height; + view = _view; + idx = _idx; + timestamp = u256(utcTime()); + block_hash = _blockHash; + sig = signHash(block_hash, _keyPair); + sig2 = signHash(fieldsWithoutBlock(), _keyPair); + } +*/ +//验证签名 +bool PBFTEngine::checkSign(PBFTMsg const& req) const +{ + h512 node_id; + if (getNodeIDByIndex(node_id, req.idx)) + { +return dev::crypto::Verify(node_id, dev::crypto::SignatureFromBytes(req.sig), req.block_hash) && +dev::crypto::Verify(node_id, dev::crypto::SignatureFromBytes(req.sig2), req.fieldsWithoutBlock()); + } + return false; +} + +//handleP2PMessage()-->onRecvPBFTMessage()、handlePartiallyPrepare()、onReceiveGetMissedTxsRequest()、onReceiveMissedTxsResponse() +void PBFTEngine::handleP2PMessage( + NetworkException _exception, std::shared_ptr _session, P2PMessage::Ptr _message) +{ +//pushValidPBFTMsgIntoQueue +onRecvPBFTMessage(_exception, _session, _message); + +//PartiallyPreparePacket:handlePartiallyPrepare()补充稀疏区块的交易,即PrepareReq请求中的区块;处理完后,调用execPrepareAndGenerateSignMsg() +m_prepareWorker->enqueue([self, _session, _message]() { +pbftEngine->handlePartiallyPrepare(_session, _message); +}); + +//GetMissedTxsPacket:receive getMissedPacket request, response missed transactions +m_messageHandler->enqueue([self, _session, _message]() { +pbftEngine->onReceiveGetMissedTxsRequest(_session, _message); +}); + + +// MissedTxsPacket:receive missed transactions, fill block +m_messageHandler->enqueue([self, _session, _message]() { +pbftEngine->onReceiveMissedTxsResponse(_session, _message); + +}); +} + +void PBFTEngine::initPBFTEnv(unsigned view_timeout) +{ + m_consensusBlockNumber = 0; + m_view = m_toView = 0; + m_leaderFailed = false; + auto block = m_blockChain->getBlockByNumber(m_blockChain->number()); + if (!block) + { + PBFTENGINE_LOG(FATAL) << "can't find latest block"; + } + m_timeManager.initTimerManager(view_timeout); + reportBlock(*block); + initBackupDB(); + PBFTENGINE_LOG(INFO) << "[PBFT init env successfully]"; +} +``` + +``` +/// start a new thread to handle the network-receivied message +void PBFTEngine::workLoop() +{ +std::pair ret = m_msgQueue.tryPop(c_PopWaitSeconds); +handleMsg(ret.second); +/// timeout not triggered by fast view change +checkTimeout(); + +} + +//workLoop()-->handleMsg() +//主要实现从队列PBFTMsgPacket取出调用pbftMsg,然后调用handleMsg()进行处理 +void PBFTEngine::handleMsg(PBFTMsgPacket::Ptr pbftMsg) +{ + Guard l(m_mutex); + std::shared_ptr pbft_msg; + bool succ = false; + switch (pbftMsg->packet_id) + { + case PrepareReqPacket: + { + PrepareReq::Ptr prepare_req = std::make_shared(); + succ = handlePrepareMsg(prepare_req, *pbftMsg); + pbft_msg = prepare_req; + break; + } + case SignReqPacket: + { + SignReq::Ptr req = std::make_shared(); + succ = handleSignMsg(req, *pbftMsg); + pbft_msg = req; + break; + } + case CommitReqPacket: + { + CommitReq::Ptr req = std::make_shared(); + succ = handleCommitMsg(req, *pbftMsg); + pbft_msg = req; + break; + } + case ViewChangeReqPacket: + { + std::shared_ptr req = std::make_shared(); + succ = handleViewChangeMsg(req, *pbftMsg); + pbft_msg = req; + break; + } + default: + { + PBFTENGINE_LOG(DEBUG) << LOG_DESC("handleMsg: Err pbft message") + << LOG_KV("from", pbftMsg->node_idx) << LOG_KV("nodeIdx", nodeIdx()) + << LOG_KV("myNode", m_keyPair.pub().abridged()); + return; + } + } + + if (!needForwardMsg(succ, pbftMsg, *pbft_msg)) + { + return; + } + forwardMsg(pbftMsg, *pbft_msg); +} + +bool PBFTEngine::handlePrepareMsg(PrepareReq::Ptr prepare_req, PBFTMsgPacket const& pbftMsg) +{ +} +bool PBFTEngine::handlePrepareMsg(PrepareReq::Ptr prepareReq, std::string const& endpoint) +{ +} + +/** + * @brief: 1. decode the network-received PBFTMsgPacket to signReq + * 2. check the validation of the signReq + * 3. submit the block into blockchain if the size of collected signReq and + * commitReq is over 2/3 + * @param sign_req: return value, the decoded signReq + * @param pbftMsg: the network-received PBFTMsgPacket + */ +bool PBFTEngine::handleSignMsg(SignReq::Ptr sign_req, PBFTMsgPacket const& pbftMsg) +{ +} + +/** + * @brief : 1. decode the network-received message into commitReq + * 2. check the validation of the commitReq + * 3. add the valid commitReq into the cache + * 4. submit to blockchain if the size of collected commitReq is over 2/3 + * @param commit_req: return value, the decoded commitReq + * @param pbftMsg: the network-received PBFTMsgPacket + */ +bool PBFTEngine::handleCommitMsg(CommitReq::Ptr commit_req, PBFTMsgPacket const& pbftMsg) +{ +} + +bool PBFTEngine::handleViewChangeMsg( + ViewChangeReq::Ptr viewChange_req, PBFTMsgPacket const& pbftMsg) +{ +} +``` + +``` +/** 数据结构 +PBFTMsgQueue m_msgQueue; +*/ +//onRecvPBFTMessage()-->pushValidPBFTMsgIntoQueue() +void PBFTEngine::pushValidPBFTMsgIntoQueue(NetworkException, std::shared_ptr session, + P2PMessage::Ptr message, std::function const& _f) +{ +PBFTMsgPacket::Ptr pbft_msg = m_pbftMsgFactory->createPBFTMsgPacket(); +bool valid = decodePBFTMsgPacket(pbft_msg, message, session); +//将pbft_msg放入队列m_msgQueue +m_msgQueue.push(pbft_msg); +} +``` + +``` +/** +// bip 152 related logic +PartiallyPBFTReqCache::Ptr m_partiallyPrepareCache = nullptr; +*/ + +//补充稀疏区块的交易,即PrepareReq请求中的区块;处理完后,调用execPrepareAndGenerateSignMsg() +bool PBFTEngine::handlePartiallyPrepare(PrepareReq::Ptr _prepareReq) +{ +// check the PartiallyPrepare +auto ret = isValidPrepare(*_prepareReq, oss); +/// update the view for given idx +updateViewMap(_prepareReq->idx, _prepareReq->view); +_prepareReq->pBlock = m_blockFactory->createBlock(); + //未来待处理的PrepareReq + if (ret == CheckResult::FUTURE) + { + // decode the partiallyBlock + _prepareReq->pBlock->decodeProposal(ref(*_prepareReq->block), true); + bool allHit = m_txPool->initPartiallyBlock(_prepareReq->pBlock); + // hit all the transactions + if (allHit) + { + // re-encode the block into the completed block(for pbft-backup consideration),先存缓存m_partiallyPrepareCache + _prepareReq->pBlock->encode(*_prepareReq->block); + m_partiallyPrepareCache->addFuturePrepareCache(_prepareReq); + return true; + } + // request missed txs for the future prepare + else + { + m_partiallyPrepareCache->addPartiallyFuturePrepare(_prepareReq); + return requestMissedTxs(_prepareReq); + } + } +//未命中缓存时,return true +if (!m_partiallyPrepareCache->addPartiallyRawPrepare(_prepareReq)) +{ + return false; +} +// decode the partiallyBlock +_prepareReq->pBlock->decodeProposal(ref(*_prepareReq->block), true); +bool allHit = m_txPool->initPartiallyBlock(_prepareReq->pBlock); +// hit all transactions +if (allHit) +{ + m_partiallyPrepareCache->transPartiallyPrepareIntoRawPrepare(); + // begin to handlePrepare + return execPrepareAndGenerateSignMsg(_prepareReq, oss); +} +return requestMissedTxs(_prepareReq); + +} +``` + +``` +// receive the GetMissedTxsPacket request and response the requested-transactions +void PBFTEngine::onReceiveGetMissedTxsRequest( + std::shared_ptr _session, P2PMessage::Ptr _message) +{ + try + { + std::shared_ptr _encodedBytes = std::make_shared(); + if (!m_partiallyPrepareCache->fetchMissedTxs(_encodedBytes, ref(*(_message->buffer())))) + { + return; + } + // response the transaction to the request node + auto p2pMsg = toP2PMessage(_encodedBytes, MissedTxsPacket); + p2pMsg->setPacketType(MissedTxsPacket); + + m_service->asyncSendMessageByNodeID(_session->nodeID(), p2pMsg, nullptr); + } + catch (std::exception const& _e) + { + } +} +``` + +``` +// MissedTxsPacket:receive missed transactions, fill block +void PBFTEngine::onReceiveMissedTxsResponse( + std::shared_ptr _session, P2PMessage::Ptr _message) +{ + try + { + Guard l(m_mutex); + RLP blockRLP(ref(*(_message->buffer()))); + // get blockHash of the response + auto blockHash = blockRLP[1].toHash(RLP::VeryStrict); + // the response is for the future prepare, + // fill the future prepare and add it to the futurePrepareCache + if (m_partiallyPrepareCache->existInFuturePrepare(blockHash)) + { + m_partiallyPrepareCache->fillFutureBlock(blockRLP); + return; + } + if (!m_partiallyPrepareCache->fillPrepareCacheBlock(blockRLP)) + { + return; + } + // handlePrepare + auto prepareReq = m_partiallyPrepareCache->partiallyRawPrepare(); + // re-encode the block into the completed block(for pbft-backup consideration) + prepareReq->pBlock->encode(*prepareReq->block); + bool ret = handlePrepareMsg(prepareReq); + // forward the completed prepare message + if (ret && m_cachedForwardMsg->count(prepareReq->block_hash)) + { + auto pbftMsg = (*m_cachedForwardMsg)[prepareReq->block_hash].second; + // forward the message + forwardPrepareMsg(pbftMsg, prepareReq); + } + m_cachedForwardMsg->erase(prepareReq->block_hash); + } +} +``` + +``` +//该函数内主要的函数调用链有:execBlock()-->broadcastSignReq()-->checkAndCommit() +bool PBFTEngine::execPrepareAndGenerateSignMsg( + PrepareReq::Ptr _prepareReq, std::ostringstream& _oss) +{ + Timer t; + Sealing workingSealing(m_blockFactory); + try + { + // update the latest time of receiving the rawPrepare and ready to execute the block + m_timeManager.m_lastAddRawPrepareTime = utcSteadyTime(); + + execBlock(workingSealing, _prepareReq, _oss); + + // update the latest execution time when processed the block execution + m_timeManager.m_lastExecTime = utcSteadyTime(); + + // old block (has already executed correctly by block sync) + if (workingSealing.p_execContext == nullptr && + workingSealing.block->getTransactionSize() > 0) + { + return false; + } + } + catch (std::exception& e) + { + return true; + } + /// whether to omit empty block;空块会触发视图转换,选举leader + if (needOmit(workingSealing)) + { + changeViewForFastViewChange(); + m_timeManager.m_changeCycle = 0; + return true; + } + + /// generate prepare request with signature of this node to broadcast + /// (can't change prepareReq since it may be broadcasted-forwarded to other nodes) + auto startT = utcTime(); + PrepareReq::Ptr sign_prepare = + std::make_shared(*_prepareReq, workingSealing, m_keyPair); + + // destroy ExecutiveContext in m_destructorThread + auto execContext = m_reqCache->prepareCache().p_execContext; + HolderForDestructor holder(std::move(execContext)); + m_destructorThread->enqueue(std::move(holder)); + + m_reqCache->addPrepareReq(sign_prepare); + /// broadcast the re-generated signReq(add the signReq to cache) + broadcastSignReq(*sign_prepare); + checkAndCommit(); + return true; +} +``` + +``` +/** + * @brief : 1. generate and broadcast signReq according to given prepareReq, + * 2. add the generated signReq into the cache + * @param req: specified PrepareReq used to generate signReq + */ +bool PBFTEngine::broadcastSignReq(PrepareReq const& req) +{ + SignReq::Ptr sign_req = std::make_shared(req, m_keyPair, nodeIdx()); + bytes sign_req_data; + sign_req->encode(sign_req_data); + bool succ = broadcastMsg(SignReqPacket, *sign_req, ref(sign_req_data)); + m_reqCache->addSignReq(sign_req); + return succ; +} +``` + +``` +void PBFTEngine::checkAndCommit() +{ + auto minValidNodeSize = minValidNodes(); + size_t sign_size = + m_reqCache->getSigCacheSize(m_reqCache->prepareCache().block_hash, minValidNodeSize); + /// must be equal to minValidNodes:in case of callback checkAndCommit repeatly in a round of + /// PBFT consensus + if (sign_size == minValidNodeSize) + { + //判断一些条件后,执行checkAndSave保存区块数据 + checkAndSave(); + } +} +``` + +``` +/// if collect >= 2/3 SignReq and CommitReq, then callback this function to commit block +/// check whether view and height is valid, if valid, then commit the block and clear the context +void PBFTEngine::checkAndSave() +{ + auto start_commit_time = utcTime(); + auto record_time = utcTime(); + auto minValidNodeSize = minValidNodes(); + size_t sign_size = + m_reqCache->getSigCacheSize(m_reqCache->prepareCache().block_hash, minValidNodeSize); + size_t commit_size = + m_reqCache->getCommitCacheSize(m_reqCache->prepareCache().block_hash, minValidNodeSize); + if (sign_size >= minValidNodeSize && commit_size >= minValidNodeSize) + { + /// Block block(m_reqCache->prepareCache().block); + std::shared_ptr p_block = m_reqCache->prepareCache().pBlock; + m_reqCache->generateAndSetSigList(*p_block, minValidNodes()); + /// callback block chain to commit block + CommitResult ret = m_blockChain->commitBlock(p_block, + std::shared_ptr(m_reqCache->prepareCache().p_execContext)); + auto commitBlock_time_cost = utcTime() - record_time; + + } +} +``` + +``` +//PBFTSealer::handleBlock()调用m_pbftEngine->generatePrepare(m_sealing.block); +/// sealing the generated block into prepareReq and push its to msgQueue +bool PBFTEngine::generatePrepare(dev::eth::Block::Ptr _block) +{ + // fix the deadlock cases below + // 1. the sealer has sealed enough txs and is handling the block, but stucked at the + // generatePrepare for the PBFTEngine is checking timeout and ready to change view + // 2. the PBFTEngine trigger view change and release the m_mutex, the leader has been changed + // 3. the PBFTEngine calls handlePrepare for receive the PBFT prepare message from the leader, + // and handle the block + // 4. the next leader is the node-self, the PBFTEngine tries to notify the node to seal the next + // block + // 5. since the x_sealing is stucked at step 1, the PBFTEngine has been stucked at notifySeal + // Solution: + // if the sealer execute step1 (m_generatePrepare is equal to true), won't trigger notifySeal + m_generatePrepare = true; + Guard l(m_mutex); + // the leader has been changed + if (!getLeader().first || getLeader().second != nodeIdx()) + { + m_generatePrepare = false; + return true; + } + m_notifyNextLeaderSeal = false; + auto prepareReq = constructPrepareReq(_block); + + if (prepareReq->pBlock->getTransactionSize() == 0 && m_omitEmptyBlock) + { + m_leaderFailed = true; + changeViewForFastViewChange(); + m_timeManager.m_changeCycle = 0; + return true; + } + handlePrepareMsg(prepareReq); + + /// reset the block according to broadcast result + PBFTENGINE_LOG(INFO) << LOG_DESC("generateLocalPrepare") + << LOG_KV("hash", prepareReq->block_hash.abridged()) + << LOG_KV("H", prepareReq->height) << LOG_KV("nodeIdx", nodeIdx()) + << LOG_KV("myNode", m_keyPair.pub().abridged()); + m_signalled.notify_all(); + m_generatePrepare = false; + return true; +} +``` + +``` +//generatePrepare()-->constructPrepareReq() +PrepareReq::Ptr PBFTEngine::constructPrepareReq(dev::eth::Block::Ptr _block) +{ + dev::eth::Block::Ptr engineBlock = m_blockFactory->createBlock(); + *engineBlock = std::move(*_block); + PrepareReq::Ptr prepareReq = std::make_shared( + engineBlock, m_keyPair, m_view, nodeIdx(), m_enablePrepareWithTxsHash); + if (prepareReq->pBlock->transactions()->size() == 0) + { + prepareReq->isEmpty = true; + } + // the non-empty block only broadcast hash when enable-prepare-with-txs-hash + if (m_enablePrepareWithTxsHash && prepareReq->pBlock->transactions()->size() > 0) + { + // addPreRawPrepare to response to the request-sealers + m_partiallyPrepareCache->addPreRawPrepare(prepareReq); + // encode prepareReq with uncompleted transactions into sendedData + std::shared_ptr sendedData = std::make_shared(); + prepareReq->encode(*sendedData); + auto self = std::weak_ptr(shared_from_this()); + m_threadPool->enqueue([self, prepareReq, sendedData]() { + try + { + auto pbftEngine = self.lock(); + if (!pbftEngine) + { + return; + } + pbftEngine->sendPrepareMsgFromLeader( + prepareReq, ref(*sendedData), PartiallyPreparePacket); + } + catch (std::exception const& e) + { + PBFTENGINE_LOG(ERROR) << LOG_DESC("broadcastPrepare exceptioned") + << LOG_KV("errorInfo", boost::diagnostic_information(e)); + } + }); + // re-encode the block with completed transactions + prepareReq->pBlock->encode(*prepareReq->block); + } + // not enable-prepare-with-txs-hash or the empty block + else + { + auto self = std::weak_ptr(shared_from_this()); + m_threadPool->enqueue([self, prepareReq, engineBlock]() { + try + { + auto pbftEngine = self.lock(); + if (!pbftEngine) + { + return; + } + std::shared_ptr prepare_data = std::make_shared(); + prepareReq->encode(*prepare_data); + pbftEngine->sendPrepareMsgFromLeader(prepareReq, ref(*prepare_data)); + } + catch (std::exception const& e) + { + PBFTENGINE_LOG(ERROR) << LOG_DESC("broadcastPrepare exceptioned") + << LOG_KV("errorInfo", boost::diagnostic_information(e)); + } + }); + } + return prepareReq; +} +``` + +``` +/**转发消息的三种函数调用链 +//第一种:处理共识引擎的各种包类型: +PrepareReq(包含区块的请求包)、SignReq(带有区块执行结果的签名请求)、CommitReq(用于确认区块执行结果的提交请求)和ViewChangeReq(视图切换请求); + +handleMsg()-->forwardMsg()--》forwardMsgByTTL()、forwardMsgByNodeInfo() + +//第二种:针对bip 152改进,转发含交易哈希的区块,减少通信带宽 +handlePartiallyPrepare +()-->handleReceivedPartiallyPrepare +()-->forwardPrepareMsg()--》forwardMsgByTTL()、forwardMsgByNodeInfo +() + +//第三种:处理节点p2p通信,并选择节点进行转发 +handleP2PMessage +()-->onReceiveMissedTxsResponse()--》forwardMsgByTTL()、forwardMsgByNodeInfo +*/ + + +void PBFTEngine::forwardMsgByNodeInfo( + std::string const& _key, PBFTMsgPacket::Ptr _pbftMsgPacket, bytesConstRef _data) +{ +//选择未连接节点 +for (auto const& session : sessions) + { + if (remainingForwardNodes->count(session.nodeID())) + { + remainingForwardNodes->erase(session.nodeID()); + } + } +} + +// update ttl and forward the message +//转发经过每一跳,ttl减少1 +void PBFTEngine::forwardMsgByTTL( + PBFTMsgPacket::Ptr _pbftMsgPacket, PBFTMsg const& _pbftMsg, bytesConstRef _data) +{ + std::unordered_set filter; + filter.insert(_pbftMsgPacket->node_id); + /// get the origin gen node id of the request + h512 genNodeId = getSealerByIndex(_pbftMsg.idx); + if (genNodeId != h512()) + { + filter.insert(genNodeId); + } + unsigned current_ttl = _pbftMsgPacket->ttl - 1; + broadcastMsg(_pbftMsgPacket->packet_id, _pbftMsg, _data, 0, filter, current_ttl); +} +``` + +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ \ No newline at end of file diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libconsensus_raft.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libconsensus_raft.md new file mode 100644 index 000000000..5a048e065 --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libconsensus_raft.md @@ -0,0 +1,526 @@ +# libconsensus_raft 子模块 + +作者:TrustChain [微信公众号] + +libconsensus_raft:实现RAFT算法。 + + + +## 主要内容有: + ++ Raft(Replication and Fault Tolerant)是一个允许网络分区(Partition Tolerant)的一致性协议,它保证了在一个由N个节点构成的系统中有(N+1)/2(向上取整)个节点正常工作的情况下的系统的一致性;CP强一致性算法。 + ++ 节点类型:Leader、Follower以及Candidate;每个 Follower 都设置了一个随机的竞选超时时间; + ++ 任期:将时间划分为不定长度的任期Terms,Terms为连续的数字; + ++ 消息类型:VoteReq、VoteResp、Heartbeat、HeartbeatResp。Heartbeat用于维护Leader节点身份和区块数据复制。 + ++ 日志复制:leader服务器保留客户端指令到日志--》发送AppendEntries RPC给从节点--》收到从节点确认后再commit日志--》状态机执行日志,返回结果给客户端。 + +![](../../../../images/articles/sourceCode_knowledge_map/libconsensus_raft.png) + +## 涉及知识点: + ++ Common.h +``` +enum RaftPacketType : byte +{ + RaftVoteReqPacket = 0x00, + RaftVoteRespPacket = 0x01, + RaftHeartBeatPacket = 0x02, + RaftHeartBeatRespPacket = 0x03, + RaftPacketCount +}; + +enum RaftRole : byte +{ + EN_STATE_LEADER = 1, + EN_STATE_FOLLOWER = 2, + EN_STATE_CANDIDATE = 3 +}; + +enum VoteRespFlag : byte +{ + VOTE_RESP_REJECT = 0, + VOTE_RESP_LEADER_REJECT = 1, + VOTE_RESP_LASTTERM_ERROR = 2, + VOTE_RESP_OUTDATED = 3, + VOTE_RESP_FIRST_VOTE = 4, + VOTE_RESP_DISCARD = 5, + VOTE_RESP_GRANTED = 6 +}; + +enum HandleVoteResult : byte +{ + TO_LEADER, + TO_FOLLOWER, + NONE +}; + +struct RaftMsgPacket +{ + /// the index of the node that send this raft message + raft::NodeIndex nodeIdx; + /// the node id of the node that sends this raft message + Public nodeId; + /// type of the packet + RaftPacketType packetType; + /// the data of concrete request + bytes data; + /// timestamp of receive this raft message + uint64_t timestamp; + /// endpoint + std::string endpoint; +} +// +struct RaftMsg +{ +} +``` + ++ RaftEngine.cpp +``` +RaftMsgQueue m_msgQueue; + +void RaftEngine::start() +{ + initRaftEnv(); + ConsensusEngineBase::start(); +} + +void RaftEngine::workLoop() +{ + while (isWorking()) + { + auto isSyncing = m_blockSync->isSyncing(); + if (isSyncing) + { std::this_thread::sleep_for(std::chrono::milliseconds(100)); + continue; + } + + resetConfig(); + + if (m_cfgErr || m_accountType != NodeAccountType::SealerAccount) + { std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + continue; + } + + switch (getState()) + { + case RaftRole::EN_STATE_LEADER: + { + runAsLeader(); + break; + } + case RaftRole::EN_STATE_FOLLOWER: + { + runAsFollower(); + break; + } + case RaftRole::EN_STATE_CANDIDATE: + { + runAsCandidate(); + break; + } + default: + { + RAFTENGINE_LOG(WARNING) + << LOG_DESC("[#workLoop]Unknown work state") << LOG_KV("state", m_state); + break; + } + } + } +} +``` + +``` +//runAsLeader()-->runAsLeaderImp() +void RaftEngine::runAsLeader() +{ +std::unordered_map memberHeartbeatLog; +runAsLeaderImp(memberHeartbeatLog) +} + +bool RaftEngine::runAsLeaderImp(std::unordered_map& memberHeartbeatLog) +{ +//第一步:heartbeat timeout, change role to candidate +switchToCandidate(); + +//第二步:广播心跳包,并从队列取出RaftMsgPacket进行处理 + broadcastHeartbeat(); +std::pair ret = m_msgQueue.tryPop(c_PopWaitSeconds); +//各种类型的消息 +RaftVoteReqPacket--》handleVoteRequest() +RaftVoteRespPacket-->do nothing +RaftHeartBeatPacket-->handleHeartbeat() +RaftHeartBeatRespPacket-->判断从其他节点收到的心跳包是否超过一半 + +//第三步:提交未commited区块 +tryCommitUncommitedBlock(resp); +} +``` + +``` +//runAsCandidate()-->runAsCandidateImp() +void RaftEngine::runAsCandidate() +{ +//第一步:广播votereq + broadcastVoteReq(); +//第二步:投自己一票 +/// vote self +voteState.vote += 1; +//赢得选举则转为leader + if (wonElection(voteState.vote)) + { + switchToLeader(); + return; + } +while (isWorking()) + { + if (!runAsCandidateImp(voteState)) + { + break; + } +} +} + +bool RaftEngine::runAsCandidateImp(VoteState& _voteState) +{ +//如果选举超时: +if (checkElectTimeout()) +{ + if (isMajorityVote(_voteState.totalVoteCount())) + { //虽然超时,但是选举超过大多数票 + switchToCandidate(); + } + else + { + /// not receive enough vote + increaseElectTime(); + /// recover to previous term + m_term--; + switchToFollower(InvalidIndex); + } + return false; + } +//没超时,则从队列取出消息进行处理 +std::pair ret = m_msgQueue.tryPop(5); + +/** +case RaftPacketType::RaftVoteReqPacket: +if (handleVoteRequest(ret.second.nodeIdx, ret.second.nodeId, req)) +{ + switchToFollower(InvalidIndex); + return false; +} +*/ + +/** +case RaftVoteRespPacket: +HandleVoteResult handleRet = +handleVoteResponse(ret.second.nodeIdx, ret.second.nodeId, resp, _voteState); +if (handleRet == TO_LEADER) +{ + switchToLeader(); + return false; +} +else if (handleRet == TO_FOLLOWER) +{ + switchToFollower(InvalidIndex); + return false; +} +return true; +*/ + + +/** +case RaftHeartBeatPacket: +if (handleHeartbeat(ret.second.nodeIdx, ret.second.nodeId, hb)) +{ + switchToFollower(hb.leader); + return false; +} + +*/ +} +``` + +``` +//runAsFollower()-->runAsFollowerImp() +void RaftEngine::runAsFollower() +{ + while (isWorking()) + { + auto isSyncing = m_blockSync->isSyncing(); + if (isSyncing) + { + break; + } + + if (!runAsFollowerImp()) + { + break; + } std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } +} + +bool RaftEngine::runAsFollowerImp() +{ +//判断选举超时,则转为候选者角色 + if (checkElectTimeout()) +{ + switchToCandidate(); + return false; +} +//若不超时,则从队列取出消息进行处理 +std::pair ret = m_msgQueue.tryPop(5); + +/** +RaftVoteReqPacket-->handleVoteRequest() +RaftVoteRespPacket--> do nothing +RaftHeartBeatPacket--> handleHeartbeat() +*/ +} +``` + +``` +//处理RaftVoteReqPacket +bool RaftEngine::handleVoteRequest(u256 const& _from, h512 const& _node, RaftVoteReq const& _req) +{ + RAFTENGINE_LOG(DEBUG) << LOG_DESC("[#handleVoteRequest]") << LOG_KV("from", _from) + << LOG_KV("node", _node.hex().substr(0, 5)) << LOG_KV("term", _req.term) + << LOG_KV("candidate", _req.candidate); + + RaftVoteResp resp; + resp.idx = m_idx; + resp.term = m_term; + resp.height = m_highestBlock.number(); + resp.blockHash = m_highestBlock.hash(); + + resp.voteFlag = VOTE_RESP_REJECT; + resp.lastLeaderTerm = m_lastLeaderTerm; + + if (_req.term <= m_term) + { + if (m_state == EN_STATE_LEADER) + { + // include _req.term < m_term and _req.term == m_term + resp.voteFlag = VOTE_RESP_LEADER_REJECT; + RAFTENGINE_LOG(DEBUG) + << LOG_DESC("[#handleVoteRequest]Discard vreq for I'm the bigger leader") + << LOG_KV("myTerm", m_term); + } + else + { + if (_req.term == m_term) + { + // _req.term == m_term for follower and candidate + resp.voteFlag = VOTE_RESP_DISCARD; + RAFTENGINE_LOG(DEBUG) + << LOG_DESC("[#handleVoteRequest]Discard vreq for I'm already in this term") + << LOG_KV("myTerm", m_term); + } + else + { + // _req.term < m_term for follower and candidate + resp.voteFlag = VOTE_RESP_REJECT; + RAFTENGINE_LOG(DEBUG) + << LOG_DESC("[#handleVoteRequest]Discard vreq for smaller term") + << LOG_KV("myTerm", m_term); + } + sendResponse(_from, _node, RaftVoteRespPacket, resp); + return false; + } + } + + // handle lastLeaderTerm error + if (_req.lastLeaderTerm < m_lastLeaderTerm) + { + RAFTENGINE_LOG(DEBUG) + << LOG_DESC("[#handleVoteRequest]Discard vreq for smaller last leader term") + << LOG_KV("myLastLeaderTerm", m_lastLeaderTerm) + << LOG_KV("reqLastLeaderTerm", _req.lastLeaderTerm); + + resp.voteFlag = VOTE_RESP_LASTTERM_ERROR; + sendResponse(_from, _node, RaftVoteRespPacket, resp); + return false; + } + + auto currentBlockNumber = m_blockChain->number(); + { + Guard guard(m_commitMutex); + if (bool(m_uncommittedBlock)) + { + currentBlockNumber++; + } + } + + if (_req.lastBlockNumber < currentBlockNumber) + { + RAFTENGINE_LOG(DEBUG) + << LOG_DESC("[#handleVoteRequest]Discard vreq for peer's data is older than me") + << LOG_KV("myBlockNumber", currentBlockNumber) + << LOG_KV("reqBlockNumber", _req.lastBlockNumber); + + resp.voteFlag = VOTE_RESP_OUTDATED; + sendResponse(_from, _node, RaftVoteRespPacket, resp); + return false; + } + + // first vote, not change term + if (m_firstVote == InvalidIndex) + { + RAFTENGINE_LOG(DEBUG) << LOG_DESC( + "[#handleVoteRequest]Discard vreq for I'm the first time to vote"); + + m_firstVote = _req.candidate; + resp.voteFlag = VOTE_RESP_FIRST_VOTE; + sendResponse(_from, _node, RaftVoteRespPacket, resp); + return false; + } + + RAFTENGINE_LOG(DEBUG) << LOG_DESC("[#handleVoteRequest]Grant vreq"); + + m_term = _req.term; + m_leader = InvalidIndex; + m_vote = InvalidIndex; + + m_firstVote = _req.candidate; + setVote(_req.candidate); + + resp.term = m_term; + resp.voteFlag = VOTE_RESP_GRANTED; + sendResponse(_from, _node, RaftVoteRespPacket, resp); + + resetElectTimeout(); + + return true; +} +``` + +``` +//处理心跳包 +bool RaftEngine::handleHeartbeat(u256 const& _from, h512 const& _node, RaftHeartBeat const& _hb) +{ + RAFTENGINE_LOG(DEBUG) << LOG_DESC("[#handleHeartbeat]") << LOG_KV("fromIdx", _from) + << LOG_KV("fromId", _node.hex().substr(0, 5)) + << LOG_KV("hbTerm", _hb.term) << LOG_KV("hbLeader", _hb.leader); + + if (_hb.term < m_term && _hb.term <= m_lastLeaderTerm) + { + RAFTENGINE_LOG(DEBUG) << LOG_DESC("[#handleHeartbeat]Discard hb for smaller term") + << LOG_KV("myTerm", m_term) << LOG_KV("hbTerm", _hb.term) + << LOG_KV("myLastLeaderTerm", m_lastLeaderTerm); + return false; + } + + RaftHeartBeatResp resp; + resp.idx = m_idx; + resp.term = m_term; + resp.height = m_highestBlock.number(); + resp.blockHash = m_highestBlock.hash(); + resp.uncommitedBlockHash = h256(0); + + if (_hb.hasData()) + { + if (_hb.uncommitedBlockNumber - 1 == m_highestBlock.number()) + { + Guard guard(m_commitMutex); + m_uncommittedBlock = Block(_hb.uncommitedBlock); + m_uncommittedBlockNumber = _hb.uncommitedBlockNumber; + resp.uncommitedBlockHash = m_uncommittedBlock.header().hash(); + } + else + { + RAFTENGINE_LOG(WARNING) + << LOG_DESC("[#handleHeartbeat]Leader's height is not equal to mine") + << LOG_KV("leaderNextHeight", _hb.uncommitedBlockNumber) + << LOG_KV("myHeight", m_highestBlock.number()); + + return false; + } + } + sendResponse(_from, _node, RaftPacketType::RaftHeartBeatRespPacket, resp); + + bool stepDown = false; + /// _hb.term >= m_term || _hb.lastLeaderTerm > m_lastLeaderTerm + /// receive larger lastLeaderTerm, recover my term to hb term, set self to next step (follower) + if (_hb.term > m_lastLeaderTerm) + { + RAFTENGINE_LOG(DEBUG) + << LOG_DESC( + "[#handleHeartbeat]Prepare to switch to follower due to last leader term error") + << LOG_KV("lastLeaderTerm", m_lastLeaderTerm) << LOG_KV("hbLastLeader", _hb.term); + + m_term = _hb.term; + m_vote = InvalidIndex; + stepDown = true; + } + + if (_hb.term > m_term) + { + RAFTENGINE_LOG(DEBUG) + << LOG_DESC( + "[#handleHeartbeat]Prepare to switch to follower due to receive higher term") + << LOG_KV("term", m_term) << LOG_KV("hbTerm", _hb.term); + + m_term = _hb.term; + m_vote = InvalidIndex; + stepDown = true; + } + + if (m_state == EN_STATE_CANDIDATE && _hb.term >= m_term) + { + RAFTENGINE_LOG(DEBUG) + << LOG_DESC( + "[#handleHeartbeat]Prepare to switch to follower due to receive " + "higher or equal term in candidate state") + << LOG_KV("myTerm", m_term) << LOG_KV("hbTerm", _hb.term); + + m_term = _hb.term; + m_vote = InvalidIndex; + stepDown = true; + } + + clearFirstVoteCache(); + // see the leader last time + m_lastLeaderTerm = _hb.term; + + resetElectTimeout(); + + return stepDown; +} +``` + ++ RaftSealer.cpp +``` +void RaftSealer::start() +{ + m_raftEngine->start(); + Sealer::start(); +} +//处理区块 +void RaftSealer::handleBlock() +{ + resetSealingHeader(m_sealing.block->header()); + m_sealing.block->calTransactionRoot(); + if (m_sealing.block->getTransactionSize() == 0) + { + reset(); + m_raftEngine->resetLastBlockTime(); + return; + } + //提交区块 + bool succ = m_raftEngine->commit(*(m_sealing.block)); + if (!succ) + { + reset(); + } +} +``` +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ \ No newline at end of file diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libconsensus_rotating_pbft.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libconsensus_rotating_pbft.md new file mode 100644 index 000000000..71919752a --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libconsensus_rotating_pbft.md @@ -0,0 +1,521 @@ +# libconsensus_rotating_pbft 子模块 + +作者:TrustChain [微信公众号] + +libconsensus_rotating_pbft:实现rPBFT算法。 + +## 主要内容有: + ++ 节点类型:共识委员节点&验证节点; + ++ epoch_sealer_num:参与共识的节点数目;epoch_block_num:共识节点替换周期; + ++ Prepare包广播优化:根据共识节点索引,构成完全n叉树,Leader产生Prepare包后,沿着树状拓扑将Prepare包转发给其所有下属子节点;Prepare包缺失的容错策略和流量均衡策略。 + +![](../../../../images/articles/sourceCode_knowledge_map/libconsensus_rotating_pbft.png) + +## 涉及知识点: + ++ RPBFTReqCache.cpp +``` +// record the requested rawPrepareReq +std::shared_ptr> m_requestedPrepareQueue; +// maps between nodeID and prepare status +std::shared_ptr> m_nodeIDToPrepareStatus; +// maps between block hash and nodeID set +std::shared_ptr>>> +m_prepareHashToNodeID; + +void RPBFTReqCache::insertRequestedRawPrepare(dev::h256 const& _hash) +{ + WriteGuard requestedPrepareHashLock(x_requestedPrepareQueue); + if (m_requestedPrepareQueue->size() >= m_maxRequestedPrepareQueueSize) + { + m_requestedPrepareQueue->pop(); + } + m_requestedPrepareQueue->push(_hash); +} + + +// select required node to request txs +dev::h512 RPBFTReqCache::selectRequiredNodeToRequestTxs( + dev::h512 const& _expectedNodeID, PBFTMsg::Ptr _receivedRawPrepareStatus) +{ + ReadGuard l(x_nodeIDToPrepareStatus); + auto blockHash = _receivedRawPrepareStatus->block_hash; + if (!m_prepareHashToNodeID->count(blockHash) || + (*m_prepareHashToNodeID)[blockHash]->size() == 0) + { + return dev::h512(); + } + // try to find the expected node + if ((*m_prepareHashToNodeID)[blockHash]->count(_expectedNodeID)) + { + return _expectedNodeID; + } + std::srand(utcTime()); + auto iterator = (*m_prepareHashToNodeID)[blockHash]->begin(); + //迭代器iterator偏移n,其中n为整数。 + std::advance(iterator, std::rand() % (*m_prepareHashToNodeID)[blockHash]->size()); + return *iterator; +} +``` + ++ RotatingPBFTEngine.cpp +``` +/**重要数据结构 +// configured epoch size +std::atomic m_epochSize = {-1}; +// the interval(measured by block number) to adjust the sealers +std::atomic m_rotatingInterval = {-1}; + +// used to record the rotatingIntervalEnableNumber changed or not +dev::eth::BlockNumber m_rotatingIntervalEnableNumber = {-1}; + +std::shared_ptr m_treeRouter; +std::shared_ptr m_rpbftReqCache; +dev::ThreadPool::Ptr m_requestRawPrepareWorker; +dev::ThreadPool::Ptr m_rawPrepareStatusReceiver; +dev::ThreadPool::Ptr m_rawPrepareResponse; +dev::ThreadPool::Ptr m_waitToRequestRawPrepare; +*/ + +typedef uint16_t IDXTYPE; + +//选择leader +IDXTYPE RotatingPBFTEngine::selectLeader() const +{ + size_t index = (m_view + m_highestBlock.number()) % m_epochSize; + return (IDXTYPE)((m_startNodeIdx.load() + index) % m_sealersNum); +} + +//重置相关配置 +void RotatingPBFTEngine::resetConfig() +{ + PBFTEngine::resetConfig(); + // update the epochBlockNum + m_rotatingIntervalUpdated = updateRotatingInterval(); + if (m_rotatingIntervalUpdated) + { + //轮数m_rotatingRound + m_rotatingRound = + (m_blockChain->number() - m_rotatingIntervalEnableNumber) / m_rotatingInterval; + } + + // if the whole consensus node list has been changed, reset the chosen consensus node list + resetChosedConsensusNodes(); + // update fault tolerance + m_f = std::min((m_epochSize - 1) / 3, (m_sealersNum - 1) / 3); + // when reach the m_rotatingInterval, update the chosen consensus node list + chooseConsensusNodes(); + // update consensusInfo when send block status by tree-topology + updateConsensusInfo(); + + resetLocatedInConsensusNodes(); +} + +//重置共识节点 +void RotatingPBFTEngine::resetChosedConsensusNodes() +{ +// first set up +auto blockNumber = m_blockChain->number(); +if (m_startNodeIdx == -1) +{ + m_rotatingRound = (blockNumber - m_rotatingIntervalEnableNumber) / m_rotatingInterval; +} +m_startNodeIdx = m_rotatingRound % m_sealersNum; +int64_t idx = m_startNodeIdx; +std::set chosedConsensusNodes; + +ReadGuard l(m_sealerListMutex); +for (auto i = 0; i < m_epochSize; i++) +{ + chosedConsensusNodes.insert(m_sealerList[idx]); + idx = (idx + 1) % m_sealersNum; +} +} + +//选择共识节点 +void RotatingPBFTEngine::chooseConsensusNodes() +{ +// the rotatingInterval or E hasn't been set +if (m_rotatingInterval == -1 || m_epochSize == -1) +{ + return; +} +auto blockNumber = m_blockChain->number(); +// don't need to switch the consensus node for not reach the rotatingInterval +int64_t consensusRound = + blockNumber - m_rotatingIntervalEnableNumber - m_rotatingRound * m_rotatingInterval; +if (consensusRound < m_rotatingInterval) +{ + return; +} +// remove one consensus Node +auto chosedOutIdx = m_rotatingRound % m_sealersNum; +NodeID chosedOutNodeId = getNodeIDByIndex(chosedOutIdx); +//删除一个旧的共识节点 +{ +WriteGuard l(x_chosedConsensusNodes); +m_chosedConsensusNodes->erase(chosedOutNodeId); +} + +//插入新共识节点 +m_chosedConsNodeChanged = true; +// update the startIndex +m_rotatingRound += 1; +m_startNodeIdx = m_rotatingRound % m_sealersNum; +auto epochSize = std::min(m_epochSize.load(), m_sealersNum.load()); +IDXTYPE chosedInNodeIdx = (m_startNodeIdx.load() + epochSize - 1) % m_sealersNum; +NodeID chosedInNodeId = getNodeIDByIndex(chosedInNodeIdx); +size_t chosedConsensusNodesSize = 0; +{ + WriteGuard l(x_chosedConsensusNodes); + m_chosedConsensusNodes->insert(chosedInNodeId); + chosedConsensusNodesSize = m_chosedConsensusNodes->size(); +} + +} +``` + +``` +/** +//status of RawPrepareReq +P2PRawPrepareStatusPacket:工作线程m_rawPrepareStatusReceive调用函数onReceiveRawPrepareStatus() + +// receive raw prepare request from other node +RequestRawPreparePacket:工作线程m_requestRawPrepareWorker +调用函数onReceiveRawPrepareRequest() + +// receive raw prepare response from the other node +RawPrepareResponse:工作线程m_rawPrepareResponse +调用函数onReceiveRawPrepareResponse() + +default: +PBFTEngine::handleP2PMessage(_exception, _session, _message); + +*/ +void RotatingPBFTEngine::handleP2PMessage(dev::p2p::NetworkException _exception, + std::shared_ptr _session, dev::p2p::P2PMessage::Ptr _message) +{ + try + { + // disable broadcast prepare by tree + if (!m_treeRouter) + { + return PBFTEngine::handleP2PMessage(_exception, _session, _message); + } + // enable broadcast prepare by tree + switch (_message->packetType()) + { + // status of RawPrepareReq + case P2PRawPrepareStatusPacket: + m_rawPrepareStatusReceiver->enqueue([this, _session, _message]() { + this->onReceiveRawPrepareStatus(_session, _message); + }); + break; + // receive raw prepare request from other node + case RequestRawPreparePacket: + m_requestRawPrepareWorker->enqueue([this, _session, _message]() { + this->onReceiveRawPrepareRequest(_session, _message); + }); + break; + // receive raw prepare response from the other node + case RawPrepareResponse: + m_rawPrepareResponse->enqueue([this, _exception, _session, _message]() { + try + { + this->onReceiveRawPrepareResponse(_session, _message); + } + catch (std::exception const& _e) + { + + } + }); + + break; + default: + PBFTEngine::handleP2PMessage(_exception, _session, _message); + break; + } + } + catch (std::exception const& _e) + { + } +} + +``` + +``` +//P2PRawPrepareStatusPacket:工作线程m_rawPrepareStatusReceive调用函数onReceiveRawPrepareStatus()-->工作线程m_waitToRequestRawPrepare调用waitAndRequestRawPrepare() + +// receive the rawPrepareReqStatus packet and update the cachedRawPrepareStatus +void RotatingPBFTEngine::onReceiveRawPrepareStatus( + std::shared_ptr _session, P2PMessage::Ptr _message) +{ + try + { + auto pbftMsg = decodeP2PMsgIntoPBFTMsg(_session, _message); + if (!pbftMsg) + { + return; + } + if (!m_rpbftReqCache->checkReceivedRawPrepareStatus( + pbftMsg, m_view, m_blockChain->number())) + { + return; + } + m_rpbftReqCache->updateRawPrepareStatusCache(_session->nodeID(), pbftMsg); + // request rawPreparePacket to the selectedNode + auto parentNodeList = getParentNode(pbftMsg); + // the root node of the tree-topology + if (!parentNodeList || parentNodeList->size() == 0) + { + return; + } + // the node disconnected with the parent node + if (!connectWithParent(parentNodeList)) + { + if (!m_rpbftReqCache->checkAndRequestRawPrepare(pbftMsg)) + { + return; + } + requestRawPreparePacket(_session->nodeID(), pbftMsg); + } + else + { + // wait for at most m_maxRequestPrepareWaitTime before request the rawPrepare + m_waitToRequestRawPrepare->enqueue([this, pbftMsg, _session, parentNodeList] { + this->waitAndRequestRawPrepare(pbftMsg, _session->nodeID(), parentNodeList); + }); + } + } + catch (std::exception const& _e) + { + } +} +``` + +``` +/** +receive raw prepare request from other node +RequestRawPreparePacket:工作线程m_requestRawPrepareWorker +调用函数onReceiveRawPrepareRequest()-->m_service->asyncSendMessageByNodeID()返回responseRawPrepare消息 +*/ +void RotatingPBFTEngine::onReceiveRawPrepareRequest( + std::shared_ptr _session, P2PMessage::Ptr _message) +{ + try + { + PBFTMsg::Ptr pbftMsg = decodeP2PMsgIntoPBFTMsg(_session, _message); + if (!pbftMsg) + { + return; + } + auto key = pbftMsg->block_hash.hex(); + if (broadcastFilter(_session->nodeID(), PrepareReqPacket, key)) + { + return; + } + // the node doesn't connect with the targetNode + if (!m_service->getP2PSessionByNodeId(_session->nodeID())) + { + return; + } + std::shared_ptr encodedRawPrepare = std::make_shared(); + m_rpbftReqCache->responseRawPrepare(encodedRawPrepare, pbftMsg); + // response the rawPrepare + auto p2pMessage = transDataToMessage(ref(*encodedRawPrepare), PrepareReqPacket, 0); + p2pMessage->setPacketType(RawPrepareResponse); + m_service->asyncSendMessageByNodeID(_session->nodeID(), p2pMessage, nullptr); + broadcastMark(_session->nodeID(), PrepareReqPacket, key); + + } + catch (std::exception const& _e) + { + + } +} +``` + +``` +/** +// receive raw prepare response from the other node +RawPrepareResponse:工作线程m_rawPrepareResponse +调用函数onReceiveRawPrepareResponse()-->PBFTEngine +::handlePrepareMsg() + +*/ +void RotatingPBFTEngine::onReceiveRawPrepareResponse( + std::shared_ptr _session, dev::p2p::P2PMessage::Ptr _message) +{ + PBFTMsgPacket::Ptr pbftMsgPacket = m_pbftMsgFactory->createPBFTMsgPacket(); + // invalid pbftMsgPacket + if (!decodePBFTMsgPacket(pbftMsgPacket, _message, _session)) + { + return; + } + PrepareReq::Ptr prepareReq = std::make_shared(); + prepareReq->decode(ref(pbftMsgPacket->data)); + Guard l(m_mutex); + handlePrepareMsg(prepareReq, pbftMsgPacket->endpoint); +} +``` + ++ VRFBasedrPBFTEngine.cpp +``` +//重置配置 +void VRFBasedrPBFTEngine::resetConfig() +{ + PBFTEngine::resetConfig(); + // Reach consensus on new blocks, update m_shouldRotateSealers to false + if (true == m_shouldRotateSealers.load()) + { + m_shouldRotateSealers.store(false); + } + // update m_f + m_f = (m_workingSealersNum - 1) / 3; + // update according to epoch_block_num + updateRotatingInterval(); + // update according to epoch_sealer_num + bool epochUpdated = updateEpochSize(); + // first setup + if (m_blockChain->number() == 0) + { + return; + } + // The previous leader forged an illegal rotating-tx, resulting in a failed + // verification And the INTERNAL_SYSTEM_KEY_NOTIFY_ROTATE flag was set to true, the current + // leader needs to rotate workingsealers + auto notifyRotateFlagInfo = + m_blockChain->getSystemConfigByKey(dev::precompiled::INTERNAL_SYSTEM_KEY_NOTIFY_ROTATE); + bool notifyRotateFlag = false; + if (notifyRotateFlagInfo != "") + { + notifyRotateFlag = (bool)(boost::lexical_cast(notifyRotateFlagInfo)); + } + if (notifyRotateFlag) + { + m_shouldRotateSealers.store(true); + + } + // the number of workingSealers is smaller than epochSize, + // trigger rotate in case of the number of working sealers has been decreased to 0 + auto maxWorkingSealerNum = std::min(m_epochSize.load(), m_sealersNum.load()); + if (m_workingSealersNum.load() < maxWorkingSealerNum) + { + m_shouldRotateSealers.store(true); + } + // After the last batch of workingsealers agreed on m_rotatingInterval blocks, + // set m_shouldRotateSealers to true to notify VRFBasedrPBFTSealer to update workingSealer + if (epochUpdated || + (0 == (m_blockChain->number() - m_rotatingIntervalEnableNumber) % m_rotatingInterval)) + { + m_shouldRotateSealers.store(true); + } + } + +// Working sealer is elected as leader in turn +IDXTYPE VRFBasedrPBFTEngine::selectLeader() const +{ + // the m_chosedSealerList has not been initialized yet + if (m_chosedSealerList->size() == 0) + { + return MAXIDX; + } + //被选中的节点idx + int64_t selectedIdx = ((m_view + m_highestBlock.number()) % m_chosedSealerList->size()); + dev::h512 selectedNodeId; + { + ReadGuard l(x_chosedSealerList); + selectedNodeId = (*m_chosedSealerList)[selectedIdx]; + } + { + ReadGuard l(x_nodeID2Index); + return (*m_nodeID2Index)[selectedNodeId]; + } +} +``` + ++ VRFBasedrPBFTSealer.cpp +``` +/** 重要数据结构 +VRFBasedrPBFTEngine::Ptr m_vrfBasedrPBFTEngine; +TxGenerator::Ptr m_txGenerator; +// VRF public key +std::string m_vrfPublicKey; +unsigned const c_privateKeyLen = 32; +std::string m_privateKey; +*/ + +// 初始化 VRFBasedrPBFTSealer +void VRFBasedrPBFTSealer::initConsensusEngine() +{ + PBFTSealer::initConsensusEngine(); + std::string threadName = "rPBFTSeal-" + std::to_string(m_pbftEngine->groupId()); + setName(threadName); + // convert PBFTEngine into VRFBasedrPBFTEngine + m_vrfBasedrPBFTEngine = std::dynamic_pointer_cast(m_pbftEngine); + // create txGenerator + m_txGenerator = std::make_shared( + m_pbftEngine->groupId(), g_BCOSConfig.chainId(), m_txPool->maxBlockLimit() / 2); + // get private key and generate vrf-public-key + m_privateKey = + toHex(bytesConstRef{m_vrfBasedrPBFTEngine->keyPair().secret().data(), c_privateKeyLen}); + auto vrfPublicKey = curve25519_vrf_generate_key_pair(m_privateKey.c_str()); + if (vrfPublicKey == nullptr) + { + } + m_vrfPublicKey = vrfPublicKey; +} + +//生成vrf交易并追加在区块的第一个交易位置上,待预编译合约dev::precompiled::WORKING_SEALER_MGR_ADDRESS进行执行验证交易签名等; +bool VRFBasedrPBFTSealer::generateTransactionForRotating() +{ + try + { + // get VRF proof + auto blockNumber = m_blockChain->number(); + auto blockHash = m_blockChain->numberHash(blockNumber); + auto blockHashStr = toHex(blockHash); + auto vrfProofPtr = curve25519_vrf_proof(m_privateKey.c_str(), blockHashStr.c_str()); + if (!vrfProofPtr) + { + return false; + } + std::string vrfProof = vrfProofPtr; + // generate "rotateWorkingSealer" transaction + std::string interface = dev::precompiled::WSM_METHOD_ROTATE_STR; + auto generatedTx = + m_txGenerator->generateTransactionWithSig(interface, m_blockChain->number(), + dev::precompiled::WORKING_SEALER_MGR_ADDRESS, m_vrfBasedrPBFTEngine->keyPair(), + dev::eth::Transaction::Type::MessageCall, m_vrfPublicKey, blockHashStr, vrfProof); + + // put the generated transaction into the 0th position of the block transactions + // Note: must set generatedTx into the first transaction for other transactions may change + // the _sys_config_ and _sys_consensus_ + // here must use noteChange for this function will notify updating the txsCache + if (m_sealing.block->transactions()->size() > 0) + { + (*(m_sealing.block->transactions()))[0] = generatedTx; + m_sealing.block->noteChange(); + } + else + { + m_sealing.block->appendTransaction(generatedTx); + } + } + catch (const std::exception& _e) + { + return false; + } + return true; +} + +``` + +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ \ No newline at end of file diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libdevcore.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libdevcore.md new file mode 100644 index 000000000..a481948b0 --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libdevcore.md @@ -0,0 +1,47 @@ +# libdevcore 模块 +作者:TrustChain [微信公众号] + +libdevcore:提供数据编解码、Snappy解压缩、数据库、文件系统、线程池等常用工具库。 + + +## 主要内容有: + ++ 编解码:Base64编解码、RLP编解码、Snappy解压缩; + ++ 数据库:leveldb数据库的增删改查等; + ++ 文件系统:基于boost::filesystem实现的一些文件操作; + ++ 线程池:提供系统用的线程池和工作线程; + +![](../../../../images/articles/sourceCode_knowledge_map/libdevcore.png) + +## 涉及知识点: + ++ Base64.cpp:base64编解码,即3个字节24bit--》每6bit 按照字符对照表找到对应字符--》将该字符的ASCII编码(8bit)为一个字节,即24bit/6bit =4; + ++ RLP.cpp:RLP编解码; + ++ SnappyCompress.cpp:调用第三方库实现Snappy压缩和解压缩。备注:https://github.com/google/snappy + ++ BasicLevelDB.cpp、LevelDB.cpp:主要实现针对leveldb数据库增删改查的操作等; + ++ FileSystem.cpp:主要实现系统常用目录的创建等。 + ++ TreeTopology.cpp:主要用于区块同步、交易同步的树状广播; + ++ TrieCommon.cpp、TrieHash.cpp、TrieHash2.cpp:主要实现trie数据结构,获取默克尔树根等; + ++ Worker.cpp、ThreadPool.h:实现线程池、工作线程等; + + + + + +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ + + diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libdevcrypto.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libdevcrypto.md new file mode 100644 index 000000000..b5fe67944 --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libdevcrypto.md @@ -0,0 +1,285 @@ +# libdevcrypto 模块 +作者:TrustChain [微信公众号] + +libdevcrypto:提供国密和非国密的哈希算法、对称算法、非对称算法。 +## 主要内容有: + +| 密码算法 | 分类 | 国密| 非国密 | +| :----- | ----: | :----: |:----: | +| 对称算法 | 分组加密/块加密 | SM1、SM4、SM7 | DES、AES | +| 对称算法 | 序列加密/流加密 | | | +| 非对称算法 | 大数分解 | | RSA | +| 非对称算法 | 离散对数 | SM2、SM9| | +| 哈希算法| 密码杂凑、散列 | SM3 | BLAKE2、MD5、SHA-1、SHA-2 | +| 签名算法 | 私钥签名 | SM2| ECDSA | + +![](../../../../images/articles/sourceCode_knowledge_map/libdevcrypto.png) + +## 涉及知识点: ++ 椭圆曲线密码体制: + +![](../../../../images/articles/sourceCode_knowledge_map/libdevcrypto_1.png) + ++ sm2.cpp + +``` +# 签名 +bool SM2::sign(const char* originalData, int originalDataLen, const string& privateKey, + unsigned char* r, unsigned char* s) +{ + bool lresult = false; + SM3_CTX sm3Ctx; + EC_KEY* sm2Key = NULL; + unsigned char zValue[SM3_DIGEST_LENGTH]; + + size_t zValueLen; + ECDSA_SIG* signData = NULL; + string str = ""; // big int data + string _str = ""; // big int swap data + BIGNUM* res = NULL; + BN_CTX* ctx = NULL; + int len = 0; + int i = 0; + ctx = BN_CTX_new(); + + res = BN_new(); + if (res == NULL) + { + CRYPTO_LOG(ERROR) << "[SM2::sign] malloc BigNumber failed"; + goto err; + } + BN_hex2bn(&res, (const char*)privateKey.c_str()); + sm2Key = EC_KEY_new_by_curve_name(NID_sm2); + EC_KEY_set_private_key(sm2Key, res); + + zValueLen = sizeof(zValue); + if (!sm2GetZ(privateKey, (const EC_KEY*)sm2Key, zValue, zValueLen)) + { + CRYPTO_LOG(ERROR) << "[SM2::sign] Error Of Compute Z"; + goto err; + } + // SM3 Degist + SM3_Init(&sm3Ctx); + SM3_Update(&sm3Ctx, zValue, zValueLen); + SM3_Update(&sm3Ctx, originalData, originalDataLen); + SM3_Final(zValue, &sm3Ctx); + + signData = ECDSA_do_sign_ex(zValue, zValueLen, NULL, NULL, sm2Key); + if (signData == NULL) + { + CRYPTO_LOG(ERROR) << "[SM2::sign] Error Of SM2 Signature"; + goto err; + } + len = BN_bn2bin(signData->r, r); + for (i = 31; len > 0 && len != 32; --len, --i) + { + r[i] = r[len - 1]; + } + for (; i >= 0 && len != 32; --i) + { + r[i] = 0; + } + len = BN_bn2bin(signData->s, s); + for (i = 31; len > 0 && len != 32; --len, --i) + { + s[i] = s[len - 1]; + } + for (; i >= 0 && len != 32; --i) + { + s[i] = 0; + } + lresult = true; + // LOG(DEBUG)<<"r:"<r); + if (!signData->r) + { + CRYPTO_LOG(ERROR) << "[SM2::veify] ERROR of BN_bin2bn r" << LOG_KV("pubKey", pubHex); + goto err; + } + BN_bin2bn(_signData + 32, 32, signData->s); + if (!signData->s) + { + CRYPTO_LOG(ERROR) << "[SM2::veify] ERROR BN_bin2bn s" << LOG_KV("pubKey", pubHex); + goto err; + } + + if (ECDSA_do_verify(zValue, zValueLen, signData, sm2Key) != 1) + { + CRYPTO_LOG(ERROR) << "[SM2::veify] Error Of SM2 Verify" << LOG_KV("pubKey", pubHex); + goto err; + } + lresult = true; +err: + if (sm2Key) + EC_KEY_free(sm2Key); + if (pubPoint) + EC_POINT_free(pubPoint); + if (signData) + ECDSA_SIG_free(signData); + if (sm2Group) + EC_GROUP_free(sm2Group); + return lresult; +} + +``` ++ sm3.cpp + +``` +int SM3Hash::init(SM3_CTX* c) +{ + return ::SM3_Init(c); +} + +int SM3Hash::update(SM3_CTX* c, const void* data, size_t len) +{ + return ::SM3_Update(c, data, len); +} + +int SM3Hash::final(unsigned char* md, SM3_CTX* c) +{ + return ::SM3_Final(md, c); +} + +unsigned char* SM3Hash::sm3(const unsigned char* d, size_t n, unsigned char* md) +{ + return ::SM3(d, n, md); +} + +void SM3Hash::transForm(SM3_CTX* c, const unsigned char* data) +{ + ::SM3_Transform(c, data); +} + +SM3Hash& SM3Hash::getInstance() +{ + static SM3Hash sm3; + return sm3; +} + +``` + ++ sm4.cpp + +``` + +int SM4::setKey(const unsigned char* userKey, size_t length) +{ + return ::SM4_set_key(userKey, length, &key); +} + +void SM4::encrypt(const unsigned char* in, unsigned char* out) +{ + ::SM4_encrypt(in, out, &key); +} + +void SM4::decrypt(const unsigned char* in, unsigned char* out) +{ + ::SM4_decrypt(in, out, &key); +} + +void SM4::cbcEncrypt( + const unsigned char* in, unsigned char* out, size_t length, unsigned char* ivec, const int enc) +{ + unsigned char* iv = (unsigned char*)malloc(16); + std::memcpy(iv, ivec, 16); + ::SM4_cbc_encrypt(in, out, length, &key, iv, enc); + free(iv); +} + +SM4& SM4::getInstance() +{ + static SM4 sm4; + return sm4; +} + +``` + +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ + diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libethcore.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libethcore.md new file mode 100644 index 000000000..9183d778a --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libethcore.md @@ -0,0 +1,38 @@ +# libethcore 模块 +作者:TrustChain [微信公众号] + +libethcore:提供基本的交易、回执和区块定义,以及公共基础类等。 + +## 主要内容有: + ++ 交易数据结构及RLP编码 ++ 区块数据结构及RLP编码 ++ 交易收据数据结构及RLP编码 + +![](../../../../images/articles/sourceCode_knowledge_map/libethcore.png) + +## 涉及知识点: ++ RLP:递归前缀长度编码,两种类型编码:byte数组、byte数组的数组(列表)。 +1. 对于值在[0, 127]之间的单字节X,编码结果是:单字节X本身; +2. 如果byte数组X的长度||X||<=55,编码结果是:128+||X||、数组X; +3. 如果byte数组长度||X||>55,编码结果是:183(128+55)+ ||BE(||X||)||、数组长度的编码BE(||X||)、byte数组X;编码一个长度为 256(2828=0x100)的字符串时,因 256 需要至少 2 个字节存储,其高位字节为 0x10,因此RLP 编码输出为 [ 0xb7+ 2, 0x01,0x00,字节内容…]。 +4. 如果列表长度||s(X)||<=55,编码结果是:192+列表长度||s(X)||、各个子列表的编码s(X);s(X)=RLP(X0).RLP(X1).... +5. 如果列表长度s(X)>55,编码结果是:247(192 + 55)+列表长度||BE(||s(X)||)||、列表本身的编码BE(||s(X)||)、各子列表的编码s(X)。 + ++ ABI接口:ABI是Application Binary Interface的缩写,合约的接口说明。ABI有点类似于程序中的接口文档,描述了字段名称、字段类型、方法名称、参数名称、参数类型、方法返回值类型等。编写合约代码->编译合约->部署合约->执行合约。ABI参数含义如下: +1. name:函数名称; +2. type:方法类型,包括function(默认方法), constructor, fallback(缺省方法); +3. constant:布尔值,如果为true,指明方法不会修改合约字段的状态变量; +4. payable:布尔值,标明方法是否可以接收ether; +5. stateMutability:状态类型,包括pure (不读取区块链状态),view (和constant类型,只能查看,不会修改合约字段),nonpayable(和payable含义一样),payable(和payable含义一样)。其实保留payable和constant是为了向后兼容; +6. inputs:数组,描述参数的名称和类型; +7. name:参数名称; +8. type:参数类型; +9. outputs:和inputs一样,如果没有返回值,缺省是一个空数组。 + + +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libeventfilter.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libeventfilter.md new file mode 100644 index 000000000..f9a255a4a --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libeventfilter.md @@ -0,0 +1,27 @@ +# libeventfilter 模块 +作者:TrustChain [微信公众号] + +libeventfilter:事件日志过滤。 + + + +## 主要内容有: + ++ 区块链执行事件日志过滤 +![](../../../../images/articles/sourceCode_knowledge_map/libeventfilter.png) + +## 涉及知识点: + ++ 过滤参数:filterID、groupID、startBlock、endBlock、addresses、topics; + ++ 事件过滤:启停一个线程,处理客户端发来的增删过滤规则请求,处理filter进行处理,有两个回调函数: +``` + +std::function _respCallback +std::function _sessionCheckerCallback +``` +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ \ No newline at end of file diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libexecutive.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libexecutive.md new file mode 100644 index 000000000..95c824e91 --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libexecutive.md @@ -0,0 +1,425 @@ +# libexecutive 模块 + +作者:TrustChain [微信公众号] + +libexecutive:一笔交易的执行逻辑,包括合约部署。 + + + +## 主要内容有: + ++ 交易的执行; + ++ 合约部署。 + +![](../../../../images/articles/sourceCode_knowledge_map/libexecutive.png) + + +## 涉及知识点: + ++ Common.h +``` +/** 重要数据结构 +/// set parameters and functions for the evm call +struct CallParameters +{ + Address senderAddress; /// address of the transaction sender + Address codeAddress; /// address of the contract + Address receiveAddress; /// address of the transaction receiver + u256 valueTransfer; /// transferred wei between the sender and receiver + u256 apparentValue; + u256 gas; + bytesConstRef data; /// transaction data + bool staticCall = false; /// only true when the transaction is a message call +}; + +/// the information related to the EVM +class EnvInfo +{ +private: + eth::BlockHeader m_headerInfo; + CallBackFunction m_numberHash; + u256 m_gasUsed; + std::shared_ptr m_executiveEngine; +} +*/ +``` + ++ EVMHostContext.cpp +``` +//创建合约的交易 +//EVMHostContext::create-->调用Executive::createOpcode() +evmc_result EVMHostContext::create( + u256 const& _endowment, u256& io_gas, bytesConstRef _code, evmc_opcode _op, u256 _salt) +{ + Executive e{m_s, envInfo(), m_freeStorage, depth() + 1}; + // Note: When create initializes Executive, the flags of evmc context must be passed in + bool result = false; + if (_op == evmc_opcode::OP_CREATE) + result = e.createOpcode(myAddress(), _endowment, gasPrice(), io_gas, _code, origin()); + else + { + // TODO: when new CREATE opcode added, this logic maybe affected + assert(_op == evmc_opcode::OP_CREATE2); + result = + e.create2Opcode(myAddress(), _endowment, gasPrice(), io_gas, _code, origin(), _salt); + } + + if (!result) + { + go(depth(), e); + e.accrueSubState(sub()); + } + io_gas = e.gas(); + evmc_result evmcResult; + generateCreateResult(&evmcResult, transactionExceptionToEvmcStatusCode(e.getException()), + io_gas, e.takeOutput(), e.newAddress()); + return evmcResult; +} + +//执行交易逻辑,调用go() +evmc_result EVMHostContext::call(CallParameters& _p) +{ + Executive e{m_s, envInfo(), m_freeStorage, depth() + 1}; + // Note: When create initializes Executive, the flags of evmc context must be passed in + if (!e.call(_p, gasPrice(), origin())) + { + go(depth(), e); + e.accrueSubState(sub()); + } + _p.gas = e.gas(); + + evmc_result evmcResult; + generateCallResult(&evmcResult, transactionExceptionToEvmcStatusCode(e.getException()), _p.gas, + e.takeOutput()); + return evmcResult; +} +``` + ++ EVMHostInterface.cpp +``` + /// function table +evmc_host_interface const fnTable = { + accountExists, + getStorage, + setStorage, + getBalance, + getCodeSize, + getCodeHash, + copyCode, + selfdestruct, + call, + getTxContext, + getBlockHash, + log, +}; + +} // namespace + +const evmc_host_interface* getHostInterface() +{ + return &fnTable; +} + + //evmc_host_interface()-->fnTable-->create()--》EVMHostContext::create()-->调用Executive::createOpcode() +evmc_result create(EVMHostContext& _env, evmc_message const* _msg) noexcept +{ + u256 gas = _msg->gas; + u256 value = fromEvmC(_msg->value); + bytesConstRef init = {_msg->input_data, _msg->input_size}; + u256 salt = fromEvmC(_msg->create2_salt); + evmc_opcode opcode = + _msg->kind == EVMC_CREATE ? evmc_opcode::OP_CREATE : evmc_opcode::OP_CREATE2; + + // EVMHostContext::create takes the sender address from .myAddress(). + assert(fromEvmC(_msg->sender) == _env.myAddress()); + //调用EVMHostContext::create() + return _env.create(value, gas, init, opcode, salt); +} + + +//执行交易:call() +//执行创建合约的交易:create() +evmc_result call(evmc_host_context* _context, const evmc_message* _msg) noexcept +{ + // gas maybe smaller than 0 since outside gas is u256 and evmc_message is int64_t + // so gas maybe smaller than 0 in some extreme cases + // * origin code: assert(_msg->gas >= 0) + if (_msg->gas < 0) + { + EXECUTIVE_LOG(ERROR) << LOG_DESC("Gas overflow") << LOG_KV("cur gas", _msg->gas); + BOOST_THROW_EXCEPTION(eth::GasOverflow()); + } + + auto& env = static_cast(*_context); + + // Handle CREATE separately. + if (_msg->kind == EVMC_CREATE || _msg->kind == EVMC_CREATE2) + //创建合约的交易 + return create(env, _msg); + + CallParameters params; + params.gas = _msg->gas; + params.apparentValue = fromEvmC(_msg->value); + params.valueTransfer = _msg->kind == EVMC_DELEGATECALL ? 0 : params.apparentValue; + params.senderAddress = fromEvmC(_msg->sender); + params.codeAddress = fromEvmC(_msg->destination); + params.receiveAddress = _msg->kind == EVMC_CALL ? params.codeAddress : env.myAddress(); + params.data = {_msg->input_data, _msg->input_size}; + params.staticCall = (_msg->flags & EVMC_STATIC) != 0; + //调用链:evmc_host_interface()-->fnTable-->call()--》EVMHostContext::call()--》Executive::call() + return env.call(params); +} +``` + ++ EVMInstance.cpp +``` +/// The VM instance created with EVMInstance-C _create() function. +evmc_vm* m_instance = nullptr; + +std::shared_ptr EVMInstance::exec(executive::EVMHostContext& _ext, evmc_revision _rev, + evmc_message* _msg, const uint8_t* _code, size_t _code_size) +{ + auto result = std::make_shared( + m_instance->execute(m_instance, _ext.interface, &_ext, _rev, _msg, _code, _code_size)); + return result; +} +``` + ++ StateFace.h +``` +enum class StateType +{ + MptState, + StorageState +}; +//主要实现操作MptState或StorageState的相关函数操作 +class StateFace +{ +//其中两个提交状态的函数 +/// Commit all changes waiting in the address cache to the DB. +/// @param _commitBehaviour whether or not to remove empty accounts during commit. +virtual void commit() = 0; + +/// Commit levelDB data into hardisk or commit AMDB data into database (Called after commit()) +/// @param _commitBehaviour whether or not to remove empty accounts during commit. +virtual void dbCommit(h256 const& _blockHash, int64_t _blockNumber) = 0; + +} +``` + ++ VMFactory.cpp +``` +//Hera、evmone、DLL、Interpreter类型 +//https://github.com/ewasm/hera +//Hera是由C++实现的遵从EVMC接口的ewasm虚拟机。Hera内部包含了wasm VM,目前wasm的VM有多个实现版本,Hera计划支持Bineryen,WABT,WAVM(EOS也使用WAVM),目前可以完全支持Bineryen。 +std::unique_ptr VMFactory::create(VMKind _kind) +{ + switch (_kind) + { +#ifdef HERA + case VMKind::Hera: + return std::unique_ptr(new EVMInstance{evmc_create_hera()}); +#endif + case VMKind::evmone: + return std::unique_ptr(new EVMInstance{evmc_create_evmone()}); + case VMKind::DLL: + return std::unique_ptr(new EVMInstance{g_evmcCreateFn()}); +#if 0 + case VMKind::Interpreter: + return std::unique_ptr(new EVMInstance{evmc_create_interpreter()}); +#endif + default: + return std::unique_ptr(new EVMInstance{evmc_create_evmone()}); + } +} +``` + ++ Executive.cpp +``` +/** 重要数据结构 +std::shared_ptr m_s; +dev::executive::EnvInfo m_envInfo; +std::shared_ptr m_ext; +unsigned m_depth = 0; +dev::eth::Transaction::Ptr m_t; +u256 m_refunded = 0; ///< The amount of gas refunded. +*/ + +//调用EVMInstance::exec() +bool Executive::go() +{ +if (m_ext) +{ +#if ETH_TIMED_EXECUTIONS +Timer t; +#endif +try +{ + auto getEVMCMessage = [=]() -> shared_ptr { + // the block number will be larger than 0, + // can be controlled by the programmers + assert(m_ext->envInfo().number() >= 0); + constexpr int64_t int64max = std::numeric_limits::max(); + if (m_gas > int64max || m_ext->envInfo().gasLimit() > int64max) + { + EXECUTIVE_LOG(ERROR) << LOG_DESC("Gas overflow") << LOG_KV("gas", m_gas) + << LOG_KV("gasLimit", m_ext->envInfo().gasLimit()) + << LOG_KV("max gas/gasLimit", int64max); + BOOST_THROW_EXCEPTION(GasOverflow()); + } + assert(m_ext->depth() <= static_cast(std::numeric_limits::max())); + evmc_call_kind kind = m_ext->isCreate() ? EVMC_CREATE : EVMC_CALL; + uint32_t flags = m_ext->staticCall() ? EVMC_STATIC : 0; + // this is ensured by solidity compiler + assert(flags != EVMC_STATIC || kind == EVMC_CALL); // STATIC implies a CALL. + auto leftGas = static_cast(m_gas); + return shared_ptr( + new evmc_message{kind, flags, static_cast(m_ext->depth()), leftGas, + toEvmC(m_ext->myAddress()), toEvmC(m_ext->caller()), m_ext->data().data(), + m_ext->data().size(), toEvmC(m_ext->value()), toEvmC(0x0_cppui256)}); + }; + // Create VM instance. + auto vm = VMFactory::create(); + if (m_isCreation) + { + m_s->clearStorage(m_ext->myAddress()); + auto mode = toRevision(m_ext->evmSchedule()); + auto emvcMessage = getEVMCMessage(); + //调用EVMInstance::exec() + auto ret = vm->exec( + *m_ext, mode, emvcMessage.get(), m_ext->code().data(), m_ext->code().size()); + parseEVMCResult(ret); + auto outputRef = ret->output(); + if (outputRef.size() > m_ext->evmSchedule().maxCodeSize) + { + m_exceptionReason << LOG_KV("reason", "Code is too long") + << LOG_KV("size_limit", m_ext->evmSchedule().maxCodeSize) + << LOG_KV("size", outputRef.size()); + BOOST_THROW_EXCEPTION(OutOfGas()); + } + else if (outputRef.size() * m_ext->evmSchedule().createDataGas <= m_gas) + { + // When FreeStorage VM is enabled, + // the storage gas consumption of createData is not calculated additionally + if (!m_enableFreeStorage) + { + m_gas -= outputRef.size() * m_ext->evmSchedule().createDataGas; + } + } + else + { + if (m_ext->evmSchedule().exceptionalFailedCodeDeposit) + { + m_exceptionReason << LOG_KV("reason", "exceptionalFailedCodeDeposit"); + BOOST_THROW_EXCEPTION(OutOfGas()); + } + else + { + outputRef = {}; + } + } + m_s->setCode(m_ext->myAddress(), + bytes(outputRef.data(), outputRef.data() + outputRef.size())); + } + else + { + auto mode = toRevision(m_ext->evmSchedule()); + auto emvcMessage = getEVMCMessage(); + //虚拟机执行指令 + auto ret = vm->exec( + *m_ext, mode, emvcMessage.get(), m_ext->code().data(), m_ext->code().size()); + parseEVMCResult(ret); + } +} +catch (RevertInstruction& _e) +{ + revert(); + m_output = _e.output(); + m_excepted = TransactionException::RevertInstruction; +} +catch (OutOfGas& _e) +{ + revert(); + m_excepted = TransactionException::OutOfGas; +} +catch (GasOverflow const& _e) +{ + revert(); + m_excepted = TransactionException::GasOverflow; +} +catch (VMException const& _e) +{ + EXECUTIVE_LOG(TRACE) << "Safe VM Exception. " << diagnostic_information(_e); + m_gas = 0; + m_excepted = toTransactionException(_e); + revert(); +} +catch (PermissionDenied const& _e) +{ + revert(); + if (g_BCOSConfig.version() >= V2_6_0) + { + m_excepted = TransactionException::PermissionDenied; + } + else + { + m_gas = 0; + m_excepted = TransactionException::RevertInstruction; + } +} +catch (NotEnoughCash const& _e) +{ + revert(); + m_excepted = TransactionException::NotEnoughCash; +} +catch (PrecompiledError const& _e) +{ + revert(); + m_excepted = TransactionException::PrecompiledError; +} +catch (InternalVMError const& _e) +{ + using errinfo_evmcStatusCode = + boost::error_info; + EXECUTIVE_LOG(WARNING) << "Internal VM Error (" + << *boost::get_error_info(_e) << ")\n" + << diagnostic_information(_e); + revert(); + exit(1); +} +catch (Exception const& _e) +{ + // TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it + // does. + EXECUTIVE_LOG(ERROR) + << "Unexpected exception in VM. There may be a bug in this implementation. " + << diagnostic_information(_e); + exit(1); + // Another solution would be to reject this transaction, but that also + // has drawbacks. Essentially, the amount of ram has to be increased here. +} +catch (std::exception const& _e) +{ + // TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it + // does. + EXECUTIVE_LOG(ERROR) << "Unexpected std::exception in VM. Not enough RAM? " + << _e.what(); + exit(1); + // Another solution would be to reject this transaction, but that also + // has drawbacks. Essentially, the amount of ram has to be increased here. +} + +#if ETH_TIMED_EXECUTIONS +cnote << "VM took:" << t.elapsed() << "; gas used: " << (sgas - m_endGas); +#endif +} +return true; +} +``` + +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ \ No newline at end of file diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libflowlimit.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libflowlimit.md new file mode 100644 index 000000000..f60d220d3 --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libflowlimit.md @@ -0,0 +1,69 @@ +# libflowlimit 模块 + +作者:TrustChain [微信公众号] + +libflowlimit:网络流量控制。 + +## 主要内容有: + ++ 负责节点p2p网络流量控制; + +![](../../../../images/articles/sourceCode_knowledge_map/libflowlimit.png) + +## 涉及知识点: ++ 常见流量控制算法:计数器、漏桶(Leaky Bucket)、令牌桶(Token Bucket)。 + +1. 基于计数器的流量控制算法:该算法使用计数器统计一段时间内的请求数目,并在计数器超过指定阈值时,拒绝剩余请求,计数器周期性清零。 + +2. 漏桶算法:请求进入漏桶→漏桶以一定速率出水→漏桶满后,直接拒绝后续请求。 + +3. 令牌桶算法:向一定容量的令牌桶中加入令牌;如果令牌桶满,则无法添加新令牌;新请求到来时,先尝试获取令牌,若没有拿到令牌,则阻塞或直接返回;若拿到令牌,则取出令牌,系统处理请求;支持动态变更令牌添加速率以实时控制处理速率。 + + + ++ 节点和群组两个维度对流量进行控制。 + +1. 节点级别请求速率限制:限制业务到节点的总请求速率,当请求速率超过指定阈值后,节点会拒绝业务请求,避免节点过载,防止过多的请求导致节点异常;控制节点资源使用量,降低区块链节点之间的资源竞争; +2. 节点级别的流量控制:限制节点的平均出带宽,当节点平均出带宽超过设置阈值后,节点收到区块同步请求后会暂缓发送区块、拒绝收到的AMOP请求,避免区块同步、AMOP消息包发送对节点共识的影响; +3. 群组级别请求速率限制:限制业务到群组的请求速率,当请求速率超过阈值后,群组会拒绝业务请求,该功能可在大数据量突发请求的场景下保护区块链节点,控制群组资源使用量,降低群组间的资源竞争; +4. 群组级别的流量控制:限制每个群组的平均出带宽,当群组平均出带宽流量超过设置阈值后,该群组会暂停区块发送和AMOP请求包转发逻辑,优先将网络流量提供给共识模块使用。 + ++ RateLimiter.cpp:选择令牌桶算法来实现流量控制功能; +``` +//流量控制配置分别位于config.ini和group.i.ini配置文件的[flow_control] +//令牌更新的时间间隔 +m_permitsUpdateInterval = (double)1000000 / (double)m_maxQPS; +//当前时间大于上次令牌更新时间是,更新令牌数 +void RateLimiter::updatePermits(int64_t const& _now) +{ +if (_now <= m_lastPermitsUpdateTime) +{ + return; +} +int64_t increasedPermits = (double)(_now - m_lastPermitsUpdateTime) / m_permitsUpdateInterval; +m_currentStoredPermits = std::min(m_maxPermits, m_currentStoredPermits + increasedPermits); +// update last permits update time +if (m_currentStoredPermits == m_maxPermits) +{ + m_lastPermitsUpdateTime = _now; +} +else +{ + m_lastPermitsUpdateTime += increasedPermits * m_permitsUpdateInterval; +} +} +``` ++ RPCQPSLimiter.cpp +``` +//每个群组有对应的RateLimiter +std::shared_ptr> m_group2QPSLimiter; +RateLimiter::Ptr m_rpcQPSLimiter; +``` + +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ + +[3] https://mp.weixin.qq.com/s/-tYPsyvpmAIPiLG1gLX2vg \ No newline at end of file diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libinitializer.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libinitializer.md new file mode 100644 index 000000000..a4c4dcee6 --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libinitializer.md @@ -0,0 +1,245 @@ +# libinitializer 模块 +作者:TrustChain [微信公众号] + +libinitializer:初始化工作。 + +## 主要内容有: + ++ 区块链初始化工作 + +![](../../../../images/articles/sourceCode_knowledge_map/libinitializer.png) + +## 涉及知识点: + ++ BoostLogInitializer.cpp:在initLog函数和initStatLog函数里都通过调用initLogSink函数初始化一个sink进行日志打印; + + ++ GlobalConfigureInitializer.cpp: +``` +//EVM参数配置 +g_BCOSConfig.setEVMSchedule(dev::eth::FiscoBcosScheduleV3); +//data_secure 落盘加密 + g_BCOSConfig.diskEncryption.enable = _pt.get(sectionName + ".enable", false); +/// compress related option, default enable + bool enableCompress = _pt.get("p2p.enable_compress", true); +//国密算法 +bool useSMCrypto = _pt.get("chain.sm_crypto", false); +g_BCOSConfig.setUseSMCrypto(useSMCrypto); +``` ++ Initializer.cpp +``` +void Initializer::init(std::string const& _path) +{ + try + { + boost::property_tree::ptree pt; + boost::property_tree::read_ini(_path, pt); + + /// init log + m_logInitializer = std::make_shared(); + m_logInitializer->initLog(pt); + /// init global config. must init before DB, for compatibility + initGlobalConfig(pt); + + // init the statLog + if (g_BCOSConfig.enableStat()) + { + m_logInitializer->initStatLog(pt); + } + + // init certificates + m_secureInitializer = std::make_shared(); + m_secureInitializer->initConfig(pt); + + m_p2pInitializer = std::make_shared(); + m_p2pInitializer->setSSLContext( + m_secureInitializer->SSLContext(SecureInitializer::Usage::ForP2P)); + m_p2pInitializer->setKeyPair(m_secureInitializer->keyPair()); + m_p2pInitializer->initConfig(pt); + + m_rpcInitializer = std::make_shared(); + m_rpcInitializer->setP2PService(m_p2pInitializer->p2pService()); + m_rpcInitializer->setSSLContext( + m_secureInitializer->SSLContext(SecureInitializer::Usage::ForRPC)); + /// must start RPC server here for Ledger initializer depends on AMDB, AMDB depends on RPC + m_rpcInitializer->initChannelRPCServer(pt); + + m_ledgerInitializer = std::make_shared(); + m_ledgerInitializer->setP2PService(m_p2pInitializer->p2pService()); + m_ledgerInitializer->setKeyPair(m_secureInitializer->keyPair()); + m_ledgerInitializer->setChannelRPCServer(m_rpcInitializer->channelRPCServer()); + m_ledgerInitializer->initConfig(pt); + + m_rpcInitializer->setLedgerInitializer(m_ledgerInitializer); + m_rpcInitializer->initConfig(pt); + m_ledgerInitializer->startAll(); + } + catch (std::exception& e) + { + INITIALIZER_LOG(ERROR) << LOG_BADGE("Initializer") << LOG_DESC("Init failed") + << LOG_KV("EINFO", boost::diagnostic_information(e)); + ERROR_OUTPUT << LOG_BADGE("Initializer") << LOG_DESC("Init failed") + << LOG_KV("EINFO", boost::diagnostic_information(e)) << std::endl; + BOOST_THROW_EXCEPTION(e); + } +} +``` + ++ LedgerInitializer.cpp +``` + +bool LedgerInitializer::initLedger( + dev::GROUP_ID const& _groupId, std::string const& _dataDir, std::string const& _configFileName) +{ + if (m_ledgerManager->isLedgerExist(_groupId)) + { + // Already initialized + return true; + } + INITIALIZER_LOG(INFO) << "[initSingleLedger] [GroupId], LedgerConstructor: " + << std::to_string(_groupId) << LOG_KV("configFileName", _configFileName) + << LOG_KV("dataDir", _dataDir); + auto ledger = std::make_shared(m_p2pService, _groupId, m_keyPair); + ledger->setChannelRPCServer(m_channelRPCServer); + auto ledgerParams = std::make_shared(); + ledgerParams->init(_configFileName, _dataDir); + //调用Ledger.cpp 的函数Ledger::initLedger + bool succ = ledger->initLedger(ledgerParams); + if (!succ) + { + return false; + } + + m_ledgerManager->insertLedger(_groupId, ledger); + return true; +} +``` ++ P2PInitializer.cpp +``` + +/** +[p2p] +listen_ip=0.0.0.0 +listen_port=30300 +*/ +//负责节点间的P2P通信 +void P2PInitializer::initConfig(boost::property_tree::ptree const& _pt) +{ +std::string listenIP = _pt.get("p2p.listen_ip", "0.0.0.0"); +int listenPort = _pt.get("p2p.listen_port", 30300); + +//存放p2p节点 +std::map nodes; +nodes.insert( std::make_pair(NodeIPEndpoint{ip_address, port}, NodeID())); +nodes.insert( std::make_pair(NodeIPEndpoint{ipv4Endpoint[0], port}, NodeID())); +//节点黑名单crl +std::vector certBlacklist; +certBlacklist.push_back(nodeID); +//白名单 +auto whitelist = parseWhitelistFromPropertyTree(_pt); + +//p2pService >> host >> asioInterface + +//设置asioInterface +auto asioInterface = std::make_shared(); +asioInterface->setIOService(std::make_shared()); +asioInterface->setSSLContext(m_SSLContext); +asioInterface->setType(dev::network::ASIOInterface::SSL); + +//设置host +auto host = std::make_shared(); +host->setASIOInterface(asioInterface); +host->setSessionFactory(std::make_shared()); +host->setMessageFactory(messageFactory); +host->setHostPort(listenIP, listenPort); +host->setThreadPool(std::make_shared("P2P", 4)); +host->setCRL(certBlacklist); + +//设置m_p2pService +m_p2pService = std::make_shared(); +m_p2pService->setHost(host); +m_p2pService->setStaticNodes(nodes); +m_p2pService->setKeyPair(m_keyPair); +m_p2pService->setP2PMessageFactory(messageFactory); +m_p2pService->setWhitelist(whitelist); +// start the p2pService +m_p2pService->start(); +// CAL means certificate accepted list, CAL optional in config.ini +std::vector certWhitelist; +certWhitelist.push_back(nodeID); +} +``` + ++ RPCInitializer.cpp: +``` +//channel_listen_port端口使用了TCP的长连接,用心跳包检测和保持存活,通信效率较高,支持AMOP协议的点对点通信,功能相当灵活强大。 +/** +[rpc] +listen_ip=127.0.0.1 内网监听 +channel_listen_port=20200 +jsonrpc_listen_port=8545 +*/ + +//RPC 、channelMessage、TCP +ChannelRPCServer::Ptr m_channelRPCServer; +std::shared_ptr m_rpcForChannel; +ModularServer<>* m_channelRPCHttpServer; + +//控制台app+SDK<=====>节点的通信 +void RPCInitializer::initChannelRPCServer(boost::property_tree::ptree const& _pt){ + // listen ip for channel service, load from rpc.listen_ip if rpc.channel_listen_ip is not setted +int listenPort = _pt.get("rpc.channel_listen_port", 20200); +int httpListenPort = _pt.get("rpc.jsonrpc_listen_port", 8545); +//涉及节点p2p通信的参数配置 + m_channelRPCServer.reset(new ChannelRPCServer(), [](ChannelRPCServer* p) { (void)p; }); +m_channelRPCServer->setListenAddr(listenIP); +m_channelRPCServer->setListenPort(listenPort); +m_channelRPCServer->setSSLContext(m_sslContext); +m_channelRPCServer->setService(m_p2pService); +m_channelRPCServer->setNetworkStatHandler(m_networkStatHandler); +m_channelRPCServer->setNetworkBandwidthLimiter(networkBandwidth); + +//涉及节点p2p通信,实际是用到这个server,格式是channelMessage +auto server = std::make_shared(); +server->setIOService(ioService); +server->setSSLContext(m_sslContext); +server->setEnableSSL(true); +server->setBind(listenIP, listenPort); +server->setCheckCertIssuer(checkCertIssuer); +server->setMessageFactory(std::make_shared()); +m_channelRPCServer->setChannelServer(server); +} +``` + +``` + +//RPC是客户端与区块链系统交互的一套协议和接口,用户通过RPC接口可查询区块链相关信息(如块高、区块、节点连接等)和发送交易。RPC端口接受JSON-RPC格式的请求,格式比较直观清晰,采用CURL、JavaScript、Python、Go等语言都可以组装JSON格式的请求,发送到节点来处理。当然发送交易时,要在客户端实现交易签名。要注意的是,RPC连接没有做证书验证,且网络传输默认是明文的,安全性相对不高,建议只监听内网端口,用于监控、运营管理,状态查询等内部的工作流程上。目前监控脚本,区块链浏览器连接的是RPC端口。 +//RPC、jsonrpc、http +std::shared_ptr m_safeHttpServer; +ModularServer<>* m_jsonrpcHttpServer; + +//区块链浏览器+监控脚本<=====>节点的通信 +void RPCInitializer::initConfig(boost::property_tree::ptree const& _pt){ +// listen ip for jsonrpc, load from rpc.listen ip if rpc.jsonrpc_listen_ip is not setted +auto rpcEntity = new rpc::Rpc(m_ledgerInitializer, m_p2pService); +auto ipAddress = boost::asio::ip::make_address(listenIP); +m_safeHttpServer.reset(new SafeHttpServer(listenIP, httpListenPort, ipAddress.is_v6()),[](SafeHttpServer* p) { (void)p; }); +m_jsonrpcHttpServer = new ModularServer(rpcEntity); +m_jsonrpcHttpServer->addConnector(m_safeHttpServer.get()); +} +``` ++ SecureInitializer.cpp:initConfig函数根据配置选择国密或者非国密初始化; +initConfigWithSMCrypto(pt)、initConfigWithCrypto(pt); +``` + +//举例:涉及国密的ssl通信,有默认的,有针对p2p端口通信的、有针对RPC端口通信的 +m_sslContexts[Usage::Default] = gmConfig.sslContext; +m_sslContexts[Usage::ForP2P] = gmConfig.sslContext; +m_sslContexts[Usage::ForRPC] = gmConfig.sslContext; +``` + +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ \ No newline at end of file diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libinterpreter.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libinterpreter.md new file mode 100644 index 000000000..48a2030da --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libinterpreter.md @@ -0,0 +1,212 @@ +# libinterpreter 模块 + +作者:TrustChain [微信公众号] + +libinterpreter:EVM虚拟机的解释执行版实现。 + + + +## 主要内容有: + ++ Interpreter(EVM)* + +1. Instance接口:节点调用EVM的接口; + +2. Callback接口:EVM回调节点的接口; + +3. 智能合约/交易被编译成二进制文件,即EVM指令的集合。 + ++ 其他虚拟机类型:WASM、JIT、JVM。 + +![](../../../../images/articles/sourceCode_knowledge_map/libinterpreter.png) + +## 涉及知识点: + ++ Instruction.cpp +``` +static const std::map c_instructionInfo = { +{Instruction::STOP, {"STOP", 0, 0, Tier::Zero}}, +... +} + +/// Information structure for a particular instruction. +struct InstructionInfo +{ +char const* const name; ///< The name of the instruction. +int const args; ///< Number of items required on the stack for this instruction (and, for the + ///< purposes of ret, the number taken from the stack). +int const ret; ///< Number of items placed (back) on the stack by this instruction, assuming + ///< args items were removed. +Tier const gasPriceTier; ///< Tier for gas pricing. +}; +``` ++ VMConfig.h +``` +// interpreter configuration macros for development, optimizations and tracing +// +// EVM_OPTIMIZE - all optimizations off when false (TO DO - MAKE DYNAMIC) +// EVM_SWITCH_DISPATCH - dispatch via loop and switch +// EVM_JUMP_DISPATCH - dispatch via a jump table - available only on GCC +// EVM_USE_CONSTANT_POOL - constants unpacked and ready to assign to stack +// EVM_REPLACE_CONST_JUMP - pre-verified jumps to save runtime lookup +// EVM_TRACE - provides various levels of tracing + +``` + ++ VMCalls.cpp +``` +int64_t VM::verifyJumpDest(u256 const& _dest, bool _throw) +{ + // check for overflow + if (_dest <= 0x7FFFFFFFFFFFFFFF) + { + // check for within bounds and to a jump destination + // use binary search of array because hashtable collisions are exploitable + uint64_t pc = uint64_t(_dest); + if (std::binary_search(m_jumpDests.begin(), m_jumpDests.end(), pc)) + return pc; + } + if (_throw) + throwBadJumpDestination(); + return -1; +} + +``` + ++ VM.cpp +``` +/** +VMSchedule::Ptr m_vmSchedule = nullptr; +evmc_context* m_context = nullptr; +evmc_revision m_rev = EVMC_FRONTIER; +evmc_message const* m_message = nullptr; +boost::optional m_tx_context; + +// interpreter state +Instruction m_OP; // current operation +uint64_t m_PC = 0; // program counter +u256* m_SP = m_stackEnd; // stack pointer +u256* m_SPP = m_SP; // stack pointer prime (next SP) + +typedef void (VM::*MemFnPtr)(); +MemFnPtr m_bounce = nullptr; + +// space for code +bytes m_code; + +*/ + +evmc_result execute(evmc_instance* _instance, evmc_context* _context, evmc_revision _rev, + const evmc_message* _msg, uint8_t const* _code, size_t _codeSize) noexcept +{ + (void)_instance; + std::unique_ptr vm{new dev::eth::VM}; + // create vm schedule according to evmc_context + vm->createVMSchedule(_context); + evmc_result result = {}; + dev::owning_bytes_ref output; + + try + { + output = vm->exec(_context, _rev, _msg, _code, _codeSize); + result.status_code = EVMC_SUCCESS; + result.gas_left = vm->m_io_gas; + } + catch (dev::eth::RevertInstruction& ex) + { + } +} + +// interpreter entry point +owning_bytes_ref VM::exec(evmc_context* _context, evmc_revision _rev, const evmc_message* _msg, + uint8_t const* _code, size_t _codeSize) +{ + m_context = _context; + m_rev = _rev; + m_message = _msg; + m_io_gas = uint64_t(_msg->gas); + m_PC = 0; + m_pCode = _code; + m_codeSize = _codeSize; + + // trampoline to minimize depth of call stack when calling out + m_bounce = &VM::initEntry; + //m_bounce函数指针 + do + (this->*m_bounce)(); + while (m_bounce); + + return std::move(m_output); +} + + +//取到指令并调整栈空间 +void VM::fetchInstruction() +{ + m_OP = Instruction(m_code[m_PC]); + auto const metric = c_metrics[static_cast(m_OP)]; + adjustStack(metric.num_stack_arguments, metric.num_stack_returned_items); + + // FEES... + m_runGas = metric.gas_cost; + m_newMemSize = m_mem.size(); + m_copyMemSize = 0; +} +``` + +``` +// main interpreter loop and switch +//实现各种指令集的实现逻辑 +void VM::interpretCases() +{ + INIT_CASES + DO_CASES + { + CASE(CREATE2) + { + ON_OP(); + if (m_rev < EVMC_CONSTANTINOPLE) + throwBadInstruction(); + if (m_message->flags & EVMC_STATIC) + throwDisallowedStateChange(); + m_bounce = &VM::caseCreate; + } + BREAK + + CASE(CREATE) + { + ON_OP(); + if (m_message->flags & EVMC_STATIC) + throwDisallowedStateChange(); + + m_bounce = &VM::caseCreate; + } + BREAK + + CASE(DELEGATECALL) + CASE(STATICCALL) + CASE(CALL) + CASE(CALLCODE) + { + ON_OP(); + if (m_OP == Instruction::DELEGATECALL && m_rev < EVMC_HOMESTEAD) + throwBadInstruction(); + if (m_OP == Instruction::STATICCALL && m_rev < EVMC_BYZANTIUM) + throwBadInstruction(); + if (m_OP == Instruction::CALL && m_message->flags & EVMC_STATIC && m_SP[2] != 0) + throwDisallowedStateChange(); + m_bounce = &VM::caseCall; + } + BREAK +//还有各种指令集的实现逻辑 + } + WHILE_CASES +} +``` + +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ + diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libledger.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libledger.md new file mode 100644 index 000000000..972d4211b --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libledger.md @@ -0,0 +1,150 @@ +# libledger 模块 +作者:TrustChain [微信公众号] + +libledger:账本管理。 + +## 主要内容有: + ++ 负责区块链账本管理 + +![](../../../../images/articles/sourceCode_knowledge_map/libledger.png) + +## 涉及知识点: + ++ DBInitializer.cpp: + +1. 账本存储引擎:External(2.3.0版本以下支持)、LevelDB、MySQL、RocksDB、Scalable。 + +2. 落盘加密: +``` +g_BCOSConfig.diskEncryption.cipherDataKey, +g_BCOSConfig.diskEncryption.dataKey +``` +3. initLevelDBStorage: +``` +std::shared_ptr leveldbStorage = std::make_shared(); +std::shared_ptr leveldb_handler = + std::shared_ptr(pleveldb); +leveldbStorage->setDB(leveldb_handler); +m_storage = leveldbStorage; +auto tableFactoryFactory = std::make_shared(); + tableFactoryFactory->setStorage(m_storage); +m_tableFactoryFactory = tableFactoryFactory; +``` +4. recoverFromBinaryLog函数:从区块链的binlog日志恢复区块数据; +``` +//单个区块存储数据结构 +std::vector& blockData +//恢复区块数据 + _storage->commit(num + i, blockData); +``` + +5. initTableFactory2函数:binaryLogStorage将区块进行binlog存储。 +``` +binaryLogStorage->setBinaryLogger(binaryLogger); +m_storage = binaryLogStorage; +``` +6. initSQLStorage函数:用于External模式,通过RPCChannel通信和topic进行SQL查询; + +7. initRocksDBStorage函数:用于RocksDB模式; + +8. initScalableStorage函数:用于Scalable模式,包含RocksDB(stateStorage)、SQLStorage; + +9. initZdbStorage函数:用于MySQL模式; + ++ Ledger.cpp: + +1. initLedger函数:初始化账本 +``` + +bool Ledger::initLedger(std::shared_ptr _ledgerParams) +{ + BOOST_LOG_SCOPED_THREAD_ATTR( + "GroupId", boost::log::attributes::constant(std::to_string(m_groupId))); + if (!_ledgerParams) + { + return false; + } + m_param = _ledgerParams; + /// init dbInitializer + Ledger_LOG(INFO) << LOG_BADGE("initLedger") << LOG_BADGE("DBInitializer"); + m_dbInitializer = std::make_shared(m_param, m_groupId); + m_dbInitializer->setChannelRPCServer(m_channelRPCServer); + + setSDKAllowList(m_param->mutablePermissionParam().sdkAllowList); + + // m_dbInitializer + if (!m_dbInitializer) + return false; + m_dbInitializer->initStorageDB(); + /// init the DB + bool ret = initBlockChain(); + if (!ret) + return false; + dev::h256 genesisHash = m_blockChain->getBlockByNumber(0)->headerHash(); + m_dbInitializer->initState(genesisHash); + if (!m_dbInitializer->stateFactory()) + { + Ledger_LOG(ERROR) << LOG_BADGE("initLedger") + << LOG_DESC("initBlockChain Failed for init stateFactory failed"); + return false; + } + std::shared_ptr blockChain = + std::dynamic_pointer_cast(m_blockChain); + blockChain->setStateFactory(m_dbInitializer->stateFactory()); + // setSyncNum for cachedStorage + m_dbInitializer->setSyncNumForCachedStorage(m_blockChain->number()); + + // the network statistic has been enabled + if (g_BCOSConfig.enableStat()) + { + // init network statistic handler + initNetworkStatHandler(); + } + + auto channelRPCServer = std::weak_ptr(m_channelRPCServer); + m_handler = blockChain->onReady([this, channelRPCServer](int64_t number) { + LOG(INFO) << "Push block notify: " << std::to_string(m_groupId) << "-" << number; + auto channelRpcServer = channelRPCServer.lock(); + if (channelRpcServer) + { + channelRpcServer->blockNotify(m_groupId, number); + } + }); + + initNetworkBandWidthLimiter(); + initQPSLimit(); + /// init blockVerifier, txPool, sync and consensus + return (initBlockVerifier() && initTxPool() && initSync() && consensusInitFactory() && + initEventLogFilterManager()); +} +``` + ++ LedgerManager.cpp +1. 根据groupid管理账本状态等,ledgerMap存储每个groupid的账本,会根据groupid把账本状态持久化到文件中。 +``` +// map used to store the mappings between groupId and created ledger objects +std::map> m_ledgerMap; +``` + ++ LedgerParam.cpp +``` +void LedgerParam::init(const std::string& _configFilePath, const std::string& _dataPath) +{ + /// The file group.X.genesis is required, otherwise the program terminates. + /// load genesis config of group + + parseGenesisConfig(_configFilePath); + // The file group.X.ini is available by default. + std::string iniConfigFileName = _configFilePath; + boost::replace_last(iniConfigFileName, "genesis", "ini"); + + parseIniConfig(iniConfigFileName, _dataPath); +} +``` + +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ \ No newline at end of file diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libmptstate.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libmptstate.md new file mode 100644 index 000000000..2ee8d5b90 --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libmptstate.md @@ -0,0 +1,68 @@ +# libmptstate 模块 +作者:TrustChain [微信公众号] + +libmptstate:当使用MPTState时,存储区块链的状态数据。 + +## 主要内容有: + ++ 账户数据存储方式:MPT树,与以太坊一致,MPT(Merkle Patricia Trie),是一种用hash索引数据的前缀树; + ++ 分支结点(branch node):包含16个分支,以及1个value; + ++ 扩展结点(extension node):只有1个子结点;[key,value]的一个键值对,但是这里的value是其他节点的hash值. + ++ 叶子结点(leaf node):没有子结点,包含一个value。 + +![](../../../../images/articles/sourceCode_knowledge_map/libmptstate.png) + + +## 涉及知识点: + ++ Account构造函数 +``` + Account(u256 _nonce, u256 _balance, h256 _contractRoot, h256 _codeHash, Changedness _c) + : m_isAlive(true), + m_isUnchanged(_c == Unchanged), + m_nonce(_nonce), + m_balance(_balance), + m_storageRoot(_contractRoot), + m_codeHash(_codeHash) + { + assert(_contractRoot); + } +``` + ++ MemoryDB.cpp:m_main和m_aux的内存查询等操作; +``` + +std::unordered_map> m_main; +std::unordered_map> m_aux; +``` + ++ OverlayDB.cpp:继承MemoryDB.cpp,并通过commit函数持久化到DB的接口m_db; +``` + +std::shared_ptr m_db; +``` ++ State.cpp: +``` +OverlayDB m_db; +SecureTrieDB m_state; +mutable std::unordered_map m_cache; +``` + ++ TrieDB.h:MPT数据结构 +``` + +h256 m_root; +DB* m_db = nullptr; +void forceInsertNode(h256 const& _h, bytesConstRef _v) { + m_db->insert(_h, _v); + } +``` + +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ \ No newline at end of file diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libnetwork.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libnetwork.md new file mode 100644 index 000000000..a4d47e4dc --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libnetwork.md @@ -0,0 +1,336 @@ +# libnetwork 模块 +作者:TrustChain [微信公众号] + +libnetwork:网络框架,实现节点互联和消息收发逻辑,核心逻辑在Host。 + +## 主要内容有: + ++ 节点网络互联; + ++ 节点使用 SSL连接 ,保障了通信数据的机密性; + ++ 引入CA黑名单机制,可及时与作恶节点断开网络连接; + ++ 每个节点启用Host,包含server、client两部分分别与其他节点进行收发消息。 + +![](../../../../images/articles/sourceCode_knowledge_map/libnetwork.png) + +## 涉及知识点: ++ Socket.h +``` +class Socket : public SocketFace, public std::enable_shared_from_this +{ +std::shared_ptr>> m_wsSocket; +m_wsSocket = std::make_shared>>( _ioService, _sslContext); + +//三层嵌套关系--TCP_ONLY类型 +virtual bi::tcp::socket& ref() override { return m_wsSocket->next_layer().next_layer(); } + +//三层嵌套关系--SSL类型 +virtual ba::ssl::stream& sslref() override { return m_wsSocket->next_layer(); } + +//三层嵌套关系--WEBSOCKET类型:单次握手,全双工通信,二进制帧 +virtual boost::beast::websocket::stream>& wsref() override +{ + return *m_wsSocket; +} + +} +``` + ++ Session.cpp +``` +/** +Session 发送消息涉及写操作 +*/ +///< A call B, the function to call after the response is received by A. +mutable RecursiveMutex x_seq2Callback; +std::shared_ptr> m_seq2Callback; + +//写对列 +boost::heap::priority_queue, u256>, boost::heap::compare, +boost::heap::stable> m_writeQueue; + +//A发送消息给B,A收到B的回应消息后,根据seq回调handler +void Session::asyncSendMessage(Message::Ptr message, Options options, CallbackFunc callback) +{ +auto handler = std::make_shared(); +handler->callbackFunc = callback; +addSeqCallback(message->seq(), handler); +message->encode(*p_buffer); +send(p_buffer); +} + +void Session::send(std::shared_ptr _msg) +{ +//发送消息是,先把msg放入写对列 +m_writeQueue.push(make_pair(_msg, u256(utcTime()))); +write(); +} + +void Session::write() +{ +std::pair, u256> task; +task = m_writeQueue.top(); +m_writeQueue.pop(); +enter_time = task.second; +auto buffer = task.first; +auto session = shared_from_this(); +// asio::buffer referecne buffer, so buffer need alive before asio::buffer be used +//调用asyncWrite涉及到Session::onWrite单轮写操作函数 +server->asioInterface()->asyncWrite(m_socket, boost::asio::buffer(*buffer),boost::bind(&Session::onWrite, session, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, buffer)); +} + +void Session::onWrite(boost::system::error_code ec, std::size_t, std::shared_ptr) +{ +//判断状态后,再次调用write函数 +write(); +} +//===============================================// +/** +Session 接收消息涉及读操作 +*/ +void Session::doRead() +{ +auto self = std::weak_ptr(shared_from_this()); +auto asyncRead = [self](boost::system::error_code ec, std::size_t bytesTransferred) { + s->m_data.insert(s->m_data.end(), s->m_recvBuffer.begin(), + s->m_recvBuffer.begin() + bytesTransferred); + while (true) + { + Message::Ptr message = s->m_messageFactory->buildMessage(); + ssize_t result = message->decode(s->m_data.data(), s->m_data.size()); + if (result > 0){ + /// SESSION_LOG(TRACE) << "Decode success: " << result; + NetworkException e(P2PExceptionType::Success, "Success"); + //调用onMessage函数进行处理 + s->onMessage(e, message); + s->m_data.erase(s->m_data.begin(), s->m_data.begin() + result); + } + } +} + +} +/// call by doRead() to deal with mesage +void Session::onMessage(NetworkException const& e, Message::Ptr message) +{ +ResponseCallback::Ptr callbackPtr = getCallbackBySeq(message->seq()); +//消息非请求packet且回调函数非空 +server->threadPool()->enqueue([e, callback, self, message]() { +callback(e, message); +s->removeSeqCallback(message->seq()); +} +//其余情况调用默认 + server->threadPool()->enqueue([session, handler, e, message]() { handler(e, session, message); }); +} +``` + ++ PeerWhitelist.cpp:存放节点ID白名单; +``` +std::set m_whitelist; +``` + ++ Common.cpp +``` +struct ec_key_st{ + EC_GROUP *group; + EC_POINT *pub_key; + BIGNUM *priv_key; +}/*EC_KEY*/ + +struct ecp_pkey_st{ +int type; +int save_type; +int reference; +const EVP_PKEY_ASN1_METHOD *ameth; +ENGINE *engine; +union{ +char *ptr; +#ifndef OPENSSL_NO_EC + struct ec_key_st *ec; +#endif +} +} +// +/* +要序列化公钥: +将EVP_PKEY传递给EVP_PKEY_get1_EC_KEY()以获取EC_KEY. +将EC_KEY传递给EC_KEY_get0_public_key()以获取EC_POINT. +将EC_POINT传递给EC_POINT_point2oct()以获取八位字节,这只是无符号的char*. +*/ +/*要反序列化公钥: +将八位字节传递给EC_POINT_oct2point()以获取EC_POINT. +将EC_POINT传递给EC_KEY_set_public_key()以获取EC_KEY. +将EC_KEY传递给EVP_PKEY_set1_EC_KEY以获取EVP_KEY. +*/ +/*要序列化私钥: +将EVP_PKEY传递给EVP_PKEY_get1_EC_KEY()以获取EC_KEY. +将EC_KEY传递给EC_KEY_get0_private_key()以获取BIGNUM. +将BIGNUM传递给BN_bn2mpi()以获取mpi,这是一种写入unsigned char*的格式. +*/ +/*要反序列化私钥: +将mpi传递给BN_mpi2bn()以获得BIGNUM. +将BIGNUM传递给EC_KEY_set_private_key()以获取EC_KEY. +将EC_KEY传递给EVP_PKEY_set1_EC_KEY以获取EVP_KEY. +*/ +//从X509证书得到公钥 +bool dev::network::getPublicKeyFromCert(std::shared_ptr _nodeIDOut, X509* cert) +{ +EVP_PKEY* evpPublicKey = X509_get_pubkey(cert); +ec_key_st* ecPublicKey = EVP_PKEY_get1_EC_KEY(evpPublicKey); +const EC_POINT* ecPoint = EC_KEY_get0_public_key(ecPublicKey); +std::shared_ptr hex = std::shared_ptr(EC_POINT_point2hex(EC_KEY_get0_group(ecPublicKey), ecPoint, EC_KEY_get_conv_form(ecPublicKey), NULL),[](char* p) { OPENSSL_free(p); }); +} +``` + ++ ASIOInterface.cpp +``` +void ASIOInterface::asyncResolveConnect(std::shared_ptr socket, Handler_Type handler) +{ +m_resolver->async_resolve(protocol, socket->nodeIPEndpoint().address(), + to_string(socket->nodeIPEndpoint().port()), + [=](const boost::system::error_code& ec, bi::tcp::resolver::results_type results) { + +} +} + + virtual void init(std::string listenHost, uint16_t listenPort) + { + m_strand = std::make_shared(*m_ioService); + +//客户端 +m_resolver =std::make_shared(*m_ioService); + +//服务端 +m_acceptor = std::make_shared( +*m_ioService, bi::tcp::endpoint(bi::make_address(listenHost), listenPort)); +boost::asio::socket_base::reuse_address optionReuseAddress(true); +m_acceptor->set_option(optionReuseAddress); +``` + ++ Host.cpp:传输层-TCP协议-建立Socket、会话层-SSL Record Protocal【提供数据封装、压缩、加密】--建立Session、会话层--SSL HandShake Protocal--【身份认证、协商加密算法、交换加密密钥】。 + +``` +//=============服务端的功能============== +void Host::startAccept(boost::system::error_code boost_error) +{ +/** +//服务端异步监听,入参是socket和回调函数handler +//回调函数主要包含以下三个操作: +//1.验证节点证书是否过期等 +m_asioInterface->setVerifyCallback(socket, newVerifyCallback(endpointPublicKey)); +//2.ssl握手,并调用handshakeServer函数 +m_asioInterface->asyncHandshake(socket, ba::ssl::stream_base::server,boost::bind(&Host::handshakeServer, shared_from_this(), ba::placeholders::error, endpointPublicKey, socket)); + +//循环调用startAccept进行监听 +startAccept(); +*/ + +m_asioInterface->asyncAccept(); +} + +void Host::handshakeServer(const boost::system::error_code& error, + std::shared_ptr& endpointPublicKey, std::shared_ptr socket) +{ +//调用startPeerSession函数 +startPeerSession(info, socket, m_connectionHandler); +} + +void Host::start() +{ +m_asioInterface->init(m_listenHost, m_listenPort); +m_hostThread = std::make_shared([&] { +//启动一个线程,循环监听消息 +if (asioInterface()->acceptor()) +{ + startAccept(); +} +asioInterface()->run(); +} +} +``` + +``` + +//客户端实现功能--》连接服务端 +void Host::asyncConnect(NodeIPEndpoint const& _nodeIPEndpoint, + std::function)> callback) +{ +std::shared_ptr socket = m_asioInterface->newSocket(_nodeIPEndpoint); +/// if async connect timeout, close the socket directly + auto connect_timer = std::make_shared( + *(m_asioInterface->ioService()), boost::posix_time::milliseconds(m_connectTimeThre)); +connect_timer->async_wait([=](const boost::system::error_code& error) { +//超时等情况关闭socket +} +/// callback async connect + m_asioInterface->asyncResolveConnect(socket, [=](boost::system::error_code const& ec) { + +insertPendingConns(_nodeIPEndpoint); + +/// get the public key of the server during handshake +std::shared_ptr endpointPublicKey = std::make_shared(); + +m_asioInterface->setVerifyCallback(socket, newVerifyCallback(endpointPublicKey)); + +/// call handshakeClient after handshake succeed +//调用Host::handshakeClient函数 +m_asioInterface->asyncHandshake(socket, ba::ssl::stream_base::client, + boost::bind(&Host::handshakeClient, shared_from_this(), ba::placeholders::error,socket, endpointPublicKey, callback, _nodeIPEndpoint, connect_timer)); + +} +} +void Host::handshakeClient(const boost::system::error_code& error, + std::shared_ptr socket, std::shared_ptr& endpointPublicKey, + std::function)> callback, + NodeIPEndpoint _nodeIPEndpoint, std::shared_ptr timerPtr) +{ +//调用startPeerSession函数 +startPeerSession(info, socket, callback); +} +``` + +``` +//公共调用函数startPeerSession() +/** + * @brief: start peer sessions after handshake succeed(called by RLPxHandshake), + * mainly include four functions: + * 1. disconnect connecting host with invalid capability + * 2. modify m_peers && disconnect already-connected session + * 3. modify m_sessions and m_staticNodes + * 4. start new session (session->start()) + * @param _pub: node id of the connecting client + * @param _rlp: informations obtained from the client-peer during handshake + * now include protocolVersion, clientVersion, caps and listenPort + * @param _s : connected socket(used to init session object) + */ +// TODO: asyncConnect pass handle to startPeerSession, make use of it +void Host::startPeerSession(NodeInfo const& nodeInfo, std::shared_ptr const& socket, + std::function)>) +{ + auto weakHost = std::weak_ptr(shared_from_this()); + std::shared_ptr ps = + m_sessionFactory->create_session(weakHost, socket, m_messageFactory); + + auto connectionHandler = m_connectionHandler; +//启用线程池处理回调函数 + m_threadPool->enqueue([ps, connectionHandler, nodeInfo]() { + if (connectionHandler) + { + connectionHandler(NetworkException(0, ""), nodeInfo, ps); + } + else + { + HOST_LOG(WARNING) << LOG_DESC("No connectionHandler, new connection may lost"); + } + }); + HOST_LOG(INFO) << LOG_DESC("startPeerSession, Remote=") << socket->remoteEndpoint() + << LOG_KV("local endpoint", socket->localEndpoint()) + << LOG_KV("nodeID", nodeInfo.nodeID.abridged()); +} +``` +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libp2p.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libp2p.md new file mode 100644 index 000000000..eafcb0438 --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libp2p.md @@ -0,0 +1,220 @@ +# libp2p 模块 +作者:TrustChain [微信公众号] + +libp2p:p2p消息发送与处理的实现,P2P端口用于区块链节点之间的互联。 + + + +## 主要内容有: + ++ 区块链节点共识、管理网络连接、消息收发(单播、组播、广播)、状态同步; + ++ node.key 节点密钥,ECC格式;node.crt 节点证书,由CA颁发;ca.crt CA证书,CA机构提供; + ++ P2PMessage/P2PMessageRC2格式:节点与节点间通信的数据包为P2PMessage格式。根据ProtocolID分类,现在有六种:AMOP【AMOPMessage】、Topic【TopicMessage】、PBFT、BlockSync、TxPool、Raft。 + ++ AMOPMessage类型:支持SDK和节点之间、节点和节点之间的消息传递;主要实现Service::onLocalAMOPMessage函数; + ++ TopicMessage类型:该消息主要用于节点更新全局;主要实现P2PSession::onTopicMessage函数; + +![](../../../../images/articles/sourceCode_knowledge_map/libp2p.png) + +## 涉及知识点: ++ P2PMessage.cpp +``` +//AMOPMessage消息包类型枚举 +enum AMOPPacketType +{ + SendTopicSeq = 1, + RequestTopics = 2, + SendTopics = 3, +}; +/** P2PMessage Header域 共12字节 +/// m_length: 4bytes +/// pid + gid: 2bytes +/// packetType: 2bytes +/// seq: 4 bytes +*/ +/** 额外补充,因为在encode函数和decod函数有用到 +if (1 != htonl(1)) +{ //小端模式,作相应处理 } + else + { //大端模式,作相应处理 } +//大端是把字符串的尾端数据存在内存高地址处; +//小端是把字符串的尾端数据存在内存的低地址处 +uint32_t htonl(uint32_t hostlong);//32位的主机字节序转换到网络字节序 +uint16_t htons(uint16_t hostshort);//16位的主机字节序转换到网络字节序 +uint32_t ntohl(uint32_t netlong);//32位的网络字节序转换到主机字节序 uint16_t ntohs(uint16_t netshort);//16位的网络字节序转换到主机字节序 +*/ +void P2PMessage::encode(bytes& buffer) +{} +ssize_t P2PMessage::decode(const byte* buffer, size_t size) +{} +``` ++ P2PMessageRC2.cpp +``` +/// m_length(4bytes) + m_version(2bytes) + m_protocolID(2bytes) + m_groupID(2bytes) + +/// m_packetType(2bytes) + m_seq(4bytes) +// P2PMessage Header域 共16字节 +const static size_t HEADER_LENGTH = 16; +const static size_t MAX_LENGTH = 1024 * 1024; ///< The maximum length of data is 1M. +//P2PMessageRC2消息格式有调用SnappyCompress::compress压缩功能,可根据全局配置进行开启 +//encode:先压缩在encode;decode:先decode再uncompress +``` + ++ P2PMessageFactory.h + +``` +dev::network::Message::Ptr buildMessage() override +{ + auto message = std::make_shared(); + return message; +} +``` + ++ P2PSession.cpp +``` +//重要数据结构 +dev::network::SessionFace::Ptr m_session; +//维护本nodeId的topics +std::shared_ptr> m_topics; +//三种topic类型的前缀 +const std::string topicNeedVerifyPrefix = "#!$TopicNeedVerify_"; +const std::string verifyChannelPrefix = "#!$VerifyChannel_"; +const std::string pushChannelPrefix = "#!$PushChannel_"; + + +//心跳消息发送的是AMOPPacketType::SendTopicSeq类型 +void P2PSession::heartBeat() +{ +//心跳消息会自带seq号,如果对端节点收到的seq与本节点的seq不一致,则会发送AMOPPacketType::RequestTopics进行同步,这样能保证全局映射关系 +auto message =std::dynamic_pointer_cast(service->p2pMessageFactory()->buildMessage()); +message->setProtocolID(dev::eth::ProtocolID::Topic); +message->setPacketType(AMOPPacketType::SendTopicSeq); +std::shared_ptr buffer = std::make_shared(); +std::string s = boost::lexical_cast(service->topicSeq()); +buffer->assign(s.begin(), s.end()); +message->setBuffer(buffer); +m_session->asyncSendMessage(message); + +} +void P2PSession::onTopicMessage(P2PMessage::Ptr message) +{ +//处理SendTopicSeq消息类型,发送AMOPPacketType::RequestTopics +std::string s((const char*)message->buffer()->data(), message->buffer()->size()); +auto topicSeq = boost::lexical_cast(s); +//m_topicSeq 为本地seq +if (m_topicSeq != topicSeq) +{ + +auto requestTopics = std::dynamic_pointer_cast( +service->p2pMessageFactory()->buildMessage()); +requestTopics->setPacketType(AMOPPacketType::RequestTopics); +std::shared_ptr buffer = std::make_shared(); +requestTopics->setBuffer(buffer); +requestTopics->setSeq(service->p2pMessageFactory()->newSeq()); +auto self = std::weak_ptr(shared_from_this()); +dev::network::Options option; +option.timeout = 5 * 1000; // 5 seconds timeout +m_session->asyncSendMessage(requestTopics, option, +[self](NetworkException e, dev::network::Message::Ptr response) { +//处理response +auto p2pResponse = std::dynamic_pointer_cast(response); +uint32_t topicSeq = 0; +auto topicList = std::make_shared>(); +//该节点原列表orignTopicList +auto orignTopicList = session->topics(); +//从新的topics(vector)、原orignTopicList解析出topicList(set)、topicSeq +session->parseTopicList( +topics, orignTopicList, topicList, topicSeq); +//更新新的topicSeq和topicList +session->setTopics(topicSeq, topicList); +session->requestCertTopic(*topicList, topics); + +} +} + +//处理RequestTopics类型,返回AMOPPacketType::SendTopics +auto responseTopics = std::dynamic_pointer_cast( +service->p2pMessageFactory()->buildMessage()); +responseTopics>setProtocolID(((PROTOCOL_ID)dev::eth::ProtocolID::Topic)); +responseTopics->setPacketType(AMOPPacketType::SendTopics); +std::shared_ptr buffer = std::make_shared(); +std::string s = boost::lexical_cast(service->topicSeq()); +for (auto& it : service->topics()) +{ + s.append("\t"); + s.append(it); +} +buffer->assign(s.begin(), s.end()); +responseTopics->setBuffer(buffer); +responseTopics->setSeq(message->seq()); +m_session->asyncSendMessage( + responseTopics, dev::network::Options(), CallbackFunc()); +} +``` + ++ Service.cpp + +``` + +//P2PMessage的协议ID +enum ProtocolID +{ + AMOP = 1, + Topic = 2, + PBFT = 8, + BlockSync = 9, + TxPool = 10, + Raft = 11 +}; + +message->isRequestPacket()//返回包为负数 + +void Service::onConnect(dev::network::NetworkException e, dev::network::NodeInfo const& nodeInfo, + std::shared_ptr session) +{ +//调用Service::onMessage +p2pSession->session()->setMessageHandler(std::bind(&Service::onMessage, shared_from_this(),std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,p2pSessionWeakPtr)); +p2pSession->start(); + +} +//主要是这个函数根据ProtocolID进行处理 +void Service::onMessage(dev::network::NetworkException e, dev::network::SessionFace::Ptr session, + dev::network::Message::Ptr message, std::weak_ptr p2pSessionWeakPtr) +{ +} + +//dev::eth::ProtocolID::Topic 对应 [AMOP topic message] +p2pSession->onTopicMessage(p2pMessage); + +//dev::eth::ProtocolID::AMOP 对应 + m_channelNetworkStatHandler->updateAMOPInTraffic(p2pMessage->length()); +m_channelNetworkStatHandler->updateAMOPOutTraffic(message->length()); +void Service::onLocalAMOPMessage( + P2PMessage::Ptr message, CallbackFuncWithSession callback, dev::network::Options options) +{ +} + +//其余四种类型PBFT、BlockSync、TxPool、Raft调用各自的回调函数 +updateIncomingTraffic(p2pMessage); +updateOutgoingTraffic(message); +//注册回调函数 +void Service::registerHandlerByProtoclID(PROTOCOL_ID protocolID, CallbackFuncWithSession handler) +{ +} +//不同topic有不同的回调函数 +void Service::registerHandlerByTopic(std::string topic, CallbackFuncWithSession handler) +{ +} +//单播发给单个NodeId +//多播发给nodeIDsToSend里的NodeId +//广播发给session记录的NodeID +``` + +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ + diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libprecompiled.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libprecompiled.md new file mode 100644 index 000000000..2575d5a01 --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libprecompiled.md @@ -0,0 +1,1537 @@ +# libprecompiled 模块 + +作者:TrustChain [微信公众号] + +libprecompiled:预编译合约。 + + + +## 主要内容有: + ++ 执行预编译合约时首先需要根据合约地址获取到预编译合约的对象; + ++ 每个预编译合约对象都会实现call接口,预编译合约的具体逻辑在该接口中实现; + ++ call根据交易的abi编码,获取到Function Selector和参数,然后执行对应的逻辑; + ++ PrecompiledGas计算方式 + ++ Precompiled合约Gas衡量标准参考了EVM,主要包括CPU、内存和存储三个维度; + ++ 计算方式:运行时指令集合+运行时消耗的内存消耗的gas; + ++ Free Storage的Gas衡量模式,提升CPU和内存在交易Gas消耗中的占比; + ++ 在PrecopmiledGas的GasMetrics基础上引入了FreeStorageGasMetrics,并根据genesis文件的enable_free_storage配置项决定启用哪种Gas衡量模式。 + +![](../../../../images/articles/sourceCode_knowledge_map/libprecompiled.png) + +![](../../../../images/articles/sourceCode_knowledge_map/libprecompiled-extension.png) + + +## 涉及知识点: ++ libprecompiled-extension +``` +/** +基于角色的权限控制:ChainGovernance预编译合约角色分类。 +1) 联盟链委员会:由链管理员组成,新增链管理员需投票; +2) 链管理员(治理层权限):节点管理、参数配置、运维账号设置、数据管理; +3) 运维人员(运维层权限):部署合约、业务账号管理; +4) 业务用户(业务层权限):写入数据、调用接口; +5) 监管(监管层权限):读取数据、审计操作。 +*/ +PrecompiledExecResult::Ptr ChainGovernancePrecompiled::call( + ExecutiveContext::Ptr _context, bytesConstRef _param, Address const& _origin, Address const&) +{ + // parse function name + uint32_t func = getParamFunc(_param); + bytesConstRef data = getParamData(_param); + dev::eth::ContractABI abi; + bytes out; + auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); + int result = 0; + if (func == name2Selector[CGP_METHOD_GRANT_CM]) + { // grantCommitteeMember(address user) public returns (int256); + Address user; + abi.abiOut(data, user); + /** 函数grantCommitteeMember() + //grantTablePermission()操作SYS_ACCESS_TABLE、SYS_CONFIG、SYS_CONSENSUS表 + //grantCommitteeMember()-->getCommitteeTable()操作CGP_COMMITTEE_TABLE + //verifyAndRecord(Operation::GrantCommitteeMember) + */ + result = grantCommitteeMember(_context, user.hexPrefixed(), _origin); + getErrorCodeOut(callResult->mutableExecResult(), result); + } + else if (func == name2Selector[CGP_METHOD_REVOKE_CM]) + { // revokeCommitteeMember(address user) public returns (int256); + Address user; + abi.abiOut(data, user); + auto member = user.hexPrefixed(); + Table::Ptr acTable = openTable(_context, SYS_ACCESS_TABLE); + do + { + auto condition = acTable->newCondition(); + condition->EQ(SYS_AC_ADDRESS, member); + auto entries = acTable->select(SYS_ACCESS_TABLE, condition); + if (entries->size() == 0u) + { + result = CODE_COMMITTEE_MEMBER_NOT_EXIST; + CHAIN_GOVERNANCE_LOG(INFO) + << LOG_BADGE("ChainGovernance revokeMember") << LOG_DESC("member not exist") + << LOG_KV("member", member) << LOG_KV("return", result); + break; + } + result = verifyAndRecord( + _context, Operation::RevokeCommitteeMember, member, "", _origin.hexPrefixed()); + CHAIN_GOVERNANCE_LOG(INFO) + << LOG_DESC("revokeMember") << LOG_KV("origin", _origin.hexPrefixed()) + << LOG_KV("member", member) << LOG_KV("return", result); + } while (0); + + getErrorCodeOut(callResult->mutableExecResult(), result); + } + else if (func == name2Selector[CGP_METHOD_UPDATE_CM_WEIGHT]) + { // updateCommitteeMemberWeight(address user, int256 weight) + Address user; + s256 weight = 0; + abi.abiOut(data, user, weight); + std::string weightStr = boost::lexical_cast(weight); + // check the weight, must be greater than 0 + if (g_BCOSConfig.version() >= V2_7_0 && weight <= 0) + { + CHAIN_GOVERNANCE_LOG(ERROR) + << LOG_DESC("updateCommitteeMemberWeight: invalid weight, must be greater than 0") + << LOG_KV("weight", weight); + BOOST_THROW_EXCEPTION( + PrecompiledException("updateCommitteeMemberWeight failed for invalid weight \"" + + weightStr + "\", the weight must be greater than 0")); + } + /** 函数updateCommitteeMemberWeight() + //openTable(_context, SYS_ACCESS_TABLE); + //getCommitteeTable(_context); + //verifyAndRecord() + */ + int result = updateCommitteeMemberWeight(_context, user.hexPrefixed(), weightStr, _origin); + getErrorCodeOut(callResult->mutableExecResult(), result); + } + else if (func == name2Selector[CGP_METHOD_UPDATE_CM_THRESHOLD]) + { // function updateThreshold(int256 threshold) public returns (int256); + s256 weight = 0; + abi.abiOut(data, weight); + do + { + if (weight > 99 || weight < 0) + { + result = CODE_INVALID_THRESHOLD; + CHAIN_GOVERNANCE_LOG(INFO) + << LOG_BADGE("ChainGovernance updateThreshold") << LOG_DESC("invalid value") + << LOG_KV("threshold", weight) << LOG_KV("return", result); + break; + } + double threshold = boost::lexical_cast(weight) / 100; + auto committeeTable = getCommitteeTable(_context); + auto condition = committeeTable->newCondition(); + condition->EQ(CGP_COMMITTEE_TABLE_VALUE, to_string(threshold)); + auto entries = committeeTable->select(CGP_AUTH_THRESHOLD, condition); + if (entries->size() != 0u) + { + CHAIN_GOVERNANCE_LOG(INFO) + << LOG_BADGE("ChainGovernance updateMemberWeight") + << LOG_DESC("new threshold same as current") << LOG_KV("threshold", threshold) + << LOG_KV("return", result); + result = CODE_CURRENT_VALUE_IS_EXPECTED_VALUE; + break; + } + result = verifyAndRecord(_context, Operation::UpdateThreshold, + CGP_UPDATE_AUTH_THRESHOLD, to_string(threshold), _origin.hexPrefixed()); + CHAIN_GOVERNANCE_LOG(INFO) + << LOG_DESC("updateThreshold") << LOG_KV("origin", _origin.hexPrefixed()) + << LOG_KV("threshold", to_string(threshold)) << LOG_KV("return", result); + } while (0); + getErrorCodeOut(callResult->mutableExecResult(), result); + } + else if (func == name2Selector[CGP_METHOD_QUERY_VOTES_OF_CM]) + { // queryVotesOfMember(address); + Address member; + abi.abiOut(data, member); + auto resultJson = queryVotesOfMember(_context, member); + callResult->setExecResult(abi.abiIn("", resultJson)); + } + else if (func == name2Selector[CGP_METHOD_QUERY_VOTES_OF_THRESHOLD]) + { // queryVotesOfThreshold(); + auto resultJson = queryVotesOfThreshold(_context); + callResult->setExecResult(abi.abiIn("", resultJson)); + } + else if (func == name2Selector[CGP_METHOD_LIST_CM]) + { // listCommitteeMembers(); + auto resultJson = queryTablePermissions(_context, SYS_ACCESS_TABLE); + callResult->setExecResult(abi.abiIn("", resultJson)); + } + else if (func == name2Selector[CGP_METHOD_GRANT_OP]) + { // grantOperator(address) + Address user; + abi.abiOut(data, user); + result = grantOperator(_context, user.hexPrefixed(), _origin); + getErrorCodeOut(callResult->mutableExecResult(), result); + } + else if (func == name2Selector[CGP_METHOD_REVOKE_OP]) + { // revokeOperator(address) + Address user; + abi.abiOut(data, user); + result = revokeOperator(_context, user.hexPrefixed(), _origin); + getErrorCodeOut(callResult->mutableExecResult(), result); + } + else if (func == name2Selector[CGP_METHOD_LIST_OP]) + { // listOperators() + auto resultJson = listOperators(_context); + callResult->setExecResult(abi.abiIn("", resultJson)); + } + else if (func == name2Selector[CGP_METHOD_QUERY_CM_THRESHOLD]) + { // queryThreshold() public view returns (int256); + auto committeeTable = getCommitteeTable(_context); + auto entries = committeeTable->select(CGP_AUTH_THRESHOLD, committeeTable->newCondition()); + auto entry = entries->get(0); + auto threshold = + boost::lexical_cast(entry->getField(CGP_COMMITTEE_TABLE_VALUE)) * 100; + CHAIN_GOVERNANCE_LOG(INFO) << LOG_DESC("queryThreshold") << LOG_KV("return", threshold); + callResult->setExecResult(abi.abiIn("", s256(threshold))); + } + else if (func == name2Selector[CGP_METHOD_QUERY_CM_WEIGHT]) + { // queryCommitteeMemberWeight(address user) public view returns (int256); + Address user; + abi.abiOut(data, user); + string member = user.hexPrefixed(); + auto committeeTable = getCommitteeTable(_context); + auto entries = + committeeTable->select(member + CGP_WEIGTH_SUFFIX, committeeTable->newCondition()); + do + { + if (entries->size() == 0) + { + CHAIN_GOVERNANCE_LOG(INFO) + << LOG_DESC("query member not exist") << LOG_KV("member", member); + callResult->setExecResult( + abi.abiIn("", false, s256((int32_t)CODE_COMMITTEE_MEMBER_NOT_EXIST))); + break; + } + auto entry = entries->get(0); + s256 weight = boost::lexical_cast(entry->getField(CGP_COMMITTEE_TABLE_VALUE)); + CHAIN_GOVERNANCE_LOG(INFO) << LOG_DESC("memberWeight") << LOG_KV("weight", weight); + callResult->setExecResult(abi.abiIn("", true, weight)); + } while (0); + } + else if (func == name2Selector[CGP_FREEZE_ACCOUNT]) + { // function freezeAccount(address account) public returns (int256); + freezeAccount(_context, data, _origin, callResult); + } + else if (func == name2Selector[CGP_UNFREEZE_ACCOUNT]) + { // function unfreezeAccount(address account) public returns (int256); + unfreezeAccount(_context, data, _origin, callResult); + } + else if (func == name2Selector[CGP_GET_ACCOUNT_STATUS]) + { // function getAccountStatus(address account) public view returns (string); + getAccountStatus(_context, data, callResult); + } + else + { + CHAIN_GOVERNANCE_LOG(ERROR) << LOG_DESC("call undefined function") << LOG_KV("func", func); + } + return callResult; +} +``` + ++ CNSPrecompiled.cpp +``` +/** +智能合约地址的CNS别名:SYS_CNS表 +insert(name, version, address, abi) +*/ +PrecompiledExecResult::Ptr CNSPrecompiled::call( + ExecutiveContext::Ptr context, bytesConstRef param, Address const& origin, Address const&) +{ +PRECOMPILED_LOG(TRACE) << LOG_BADGE("CNSPrecompiled") << LOG_DESC("call") +<< LOG_KV("param", toHex(param)); + +// parse function name +uint32_t func = getParamFunc(param); +bytesConstRef data = getParamData(param); + +dev::eth::ContractABI abi; +auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); + +callResult->gasPricer()->setMemUsed(param.size()); + +if (func == name2Selector[CNS_METHOD_INS_STR4]) +{ // FIXME: modify insert(string,string,string,string) ==> insert(string,string,address,string) + // insert(name, version, address, abi), 4 fields in table, the key of table is name field + std::string contractName, contractVersion, contractAddress, contractAbi; +abi.abiOut(data, contractName, contractVersion, contractAddress, contractAbi); +Table::Ptr table = openTable(context, SYS_CNS); +callResult->gasPricer()->appendOperation(InterfaceOpcode::OpenTable); + +bool isValid = checkCNSParam(context, contractAddress, contractName, contractAbi); +// check exist or not +bool exist = false; +auto entries = table->select(contractName, table->newCondition()); +// Note: The selection here is only used as an internal logical judgment, +// so only calculate the computation gas +callResult->gasPricer()->appendOperation(InterfaceOpcode::Select, entries->size()); + +if (entries.get()) +{ + for (size_t i = 0; i < entries->size(); i++) + { + auto entry = entries->get(i); + if (!entry) + continue; + if (entry->getField(SYS_CNS_FIELD_VERSION) == contractVersion) + { + exist = true; + break; + } + } +} +int result = 0; +if (contractVersion.size() > CNS_VERSION_MAX_LENGTH) +{ + PRECOMPILED_LOG(ERROR) + << LOG_BADGE("CNS") << LOG_DESC("version length overflow 128") + << LOG_KV("contractName", contractName) << LOG_KV("address", contractAddress) + << LOG_KV("version", contractVersion); + result = CODE_VERSION_LENGTH_OVERFLOW; +} +else if (exist) +{ + PRECOMPILED_LOG(ERROR) + << LOG_BADGE("CNSPrecompiled") << LOG_DESC("address and version exist") + << LOG_KV("contractName", contractName) << LOG_KV("address", contractAddress) + << LOG_KV("version", contractVersion); + result = CODE_ADDRESS_AND_VERSION_EXIST; +} +else if (!isValid) +{ + PRECOMPILED_LOG(ERROR) << LOG_BADGE("CNSPrecompiled") << LOG_DESC("address invalid") + << LOG_KV("address", contractAddress); + result = CODE_ADDRESS_INVALID; +} +else +{ + // do insert + auto entry = table->newEntry(); + entry->setField(SYS_CNS_FIELD_NAME, contractName); + entry->setField(SYS_CNS_FIELD_VERSION, contractVersion); + entry->setField(SYS_CNS_FIELD_ADDRESS, contractAddress); + entry->setField(SYS_CNS_FIELD_ABI, contractAbi); + int count = table->insert(contractName, entry, std::make_shared(origin)); + if (count > 0) + { + callResult->gasPricer()->updateMemUsed(entry->size() * count); + callResult->gasPricer()->appendOperation(InterfaceOpcode::Insert, count); + } + if (count == storage::CODE_NO_AUTHORIZED) + { + PRECOMPILED_LOG(DEBUG) + << LOG_BADGE("CNSPrecompiled") << LOG_DESC("permission denied"); + result = storage::CODE_NO_AUTHORIZED; + } + else + { + PRECOMPILED_LOG(DEBUG) + << LOG_BADGE("CNSPrecompiled") << LOG_DESC("insert successfully"); + result = count; + } +} +getErrorCodeOut(callResult->mutableExecResult(), result); +} +else{ +... +} + +} +``` + ++ ConditionPrecompiled.cpp +``` +/** +{ + "e44594b9": "EQ(string,int256)", + "cd30a1d1": "EQ(string,string)", + "42f8dd31": "GE(string,int256)", + "08ad6333": "GT(string,int256)", + "b6f23857": "LE(string,int256)", + "c31c9b65": "LT(string,int256)", + "39aef024": "NE(string,int256)", + "2783acf5": "NE(string,string)", + "2e0d738a": "limit(int256)", + "7ec1cc65": "limit(int256,int256)" +} +主要实现数量关系的判断 +*/ +PrecompiledExecResult::Ptr ConditionPrecompiled::call( + ExecutiveContext::Ptr, bytesConstRef param, Address const&, Address const&) +{ + // parse function name + uint32_t func = getParamFunc(param); + bytesConstRef data = getParamData(param); + + STORAGE_LOG(DEBUG) << "func:" << std::hex << func; + + dev::eth::ContractABI abi; + + auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); + callResult->gasPricer()->setMemUsed(param.size()); + // ensured by the logic of code + assert(m_condition); + if (func == name2Selector[CONDITION_METHOD_EQ_STR_INT]) + { + // EQ(string,int256) + std::string str; + s256 num; + abi.abiOut(data, str, num); + + m_condition->EQ(str, boost::lexical_cast(num)); + callResult->gasPricer()->appendOperation(InterfaceOpcode::EQ); + } + else if(){ + + } +} +``` + ++ ConsensusPrecompiled.cpp +``` +/** +添加/删除共识节点、观察节点 +涉及表SYS_CONSENSUS +*/ +PrecompiledExecResult::Ptr ConsensusPrecompiled::call( + ExecutiveContext::Ptr context, bytesConstRef param, Address const& origin, Address const&) +{ +// parse function name +uint32_t func = getParamFunc(param); +bytesConstRef data = getParamData(param); + +dev::eth::ContractABI abi; +auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); +int count = 0; + +showConsensusTable(context); + +int result = 0; +if (func == name2Selector[CSS_METHOD_ADD_SEALER]) +{ +// addSealer(string) +std::string nodeID; +abi.abiOut(data, nodeID); +// Uniform lowercase nodeID +boost::to_lower(nodeID); + +PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ConsensusPrecompiled") << LOG_DESC("addSealer func") + << LOG_KV("nodeID", nodeID); +if (nodeID.size() != 128u) +{ +PRECOMPILED_LOG(ERROR) << LOG_BADGE("ConsensusPrecompiled") + << LOG_DESC("nodeID length error") << LOG_KV("nodeID", nodeID); +result = CODE_INVALID_NODEID; +} +else +{ +storage::Table::Ptr table = openTable(context, SYS_CONSENSUS); + +auto condition = table->newCondition(); +condition->EQ(NODE_KEY_NODEID, nodeID); +auto entries = table->select(PRI_KEY, condition); +auto entry = table->newEntry(); +entry->setField(NODE_TYPE, NODE_TYPE_SEALER); +entry->setField(PRI_COLUMN, PRI_KEY); +entry->setField(NODE_KEY_ENABLENUM, + boost::lexical_cast(context->blockInfo().number + 1)); + +if (entries.get()) +{ + if (entries->size() == 0u) + { + entry->setField(NODE_KEY_NODEID, nodeID); + count = table->insert(PRI_KEY, entry, std::make_shared(origin)); + if (count == storage::CODE_NO_AUTHORIZED) + { + PRECOMPILED_LOG(DEBUG) + << LOG_BADGE("ConsensusPrecompiled") << LOG_DESC("permission denied"); + result = storage::CODE_NO_AUTHORIZED; + } + else + { + result = count; + PRECOMPILED_LOG(DEBUG) + << LOG_BADGE("ConsensusPrecompiled") + << LOG_DESC("addSealer successfully") << LOG_KV("result", result); + } + } + else + { + count = table->update( + PRI_KEY, entry, condition, std::make_shared(origin)); + if (count == storage::CODE_NO_AUTHORIZED) + { + PRECOMPILED_LOG(DEBUG) + << LOG_BADGE("ConsensusPrecompiled") << LOG_DESC("permission denied"); + result = storage::CODE_NO_AUTHORIZED; + } + else + { + result = count; + PRECOMPILED_LOG(DEBUG) + << LOG_BADGE("ConsensusPrecompiled") + << LOG_DESC("addSealer successfully") << LOG_KV("result", result); + } + } +} +} +} +else if (func == name2Selector[CSS_METHOD_ADD_SER]) +{} +} +``` + ++ ContractLifeCyclePrecompiled.cpp +``` +/** +合约生命周期管理 +*/ +PrecompiledExecResult::Ptr ContractLifeCyclePrecompiled::call( +ExecutiveContext::Ptr context, bytesConstRef param, Address const& origin, Address const&) +{ +PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ContractLifeCyclePrecompiled"); + +// parse function name +uint32_t func = getParamFunc(param); +bytesConstRef data = getParamData(param); + +auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); + +if (func == name2Selector[METHOD_FREEZE_STR]) +{ +freeze(context, data, origin, callResult); +} +else if (func == name2Selector[METHOD_UNFREEZE_STR]) +{ +unfreeze(context, data, origin, callResult); +} +else if (func == name2Selector[METHOD_GRANT_STR]) +{ +grantManager(context, data, origin, callResult); +} +else if (func == name2Selector[METHOD_REVOKE_STR] && g_BCOSConfig.version() >= V2_7_0) +{ +revokeManager(context, data, origin, callResult); +} +else if (func == name2Selector[METHOD_QUERY_STR]) +{ +getStatus(context, data, callResult); +} +else if (func == name2Selector[METHOD_QUERY_AUTHORITY]) +{ +listManager(context, data, callResult); +} +else +{ +PRECOMPILED_LOG(ERROR) << LOG_BADGE("ContractLifeCyclePrecompiled") + << LOG_DESC("call undefined function") << LOG_KV("func", func); +} + +return callResult; +} +``` + ++ CRUDPrecompiled.cpp +``` +/** +增删改查四种sql操作 +*/ +PrecompiledExecResult::Ptr CRUDPrecompiled::call( + ExecutiveContext::Ptr context, bytesConstRef param, Address const& origin, Address const&) +{ + PRECOMPILED_LOG(TRACE) << LOG_BADGE("CRUDPrecompiled") << LOG_DESC("call") + << LOG_KV("param", toHex(param)); + // parse function name + uint32_t func = getParamFunc(param); + bytesConstRef data = getParamData(param); + + dev::eth::ContractABI abi; + auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); + callResult->gasPricer()->setMemUsed(param.size()); + + if (func == name2Selector[CRUD_METHOD_DESC_STR]) + { // desc(string) + std::string tableName; + abi.abiOut(data, tableName); + tableName = precompiled::getTableName(tableName); + Table::Ptr table = openTable(context, storage::SYS_TABLES); + callResult->gasPricer()->appendOperation(InterfaceOpcode::OpenTable); + auto entries = table->select(tableName, table->newCondition()); + // Note: Because the selected data has been returned, the memory is not updated here + callResult->gasPricer()->appendOperation(InterfaceOpcode::Select, entries->size()); + + string keyField, valueFiled; + if (entries->size() != 0) + { + auto entry = entries->get(0); + keyField = entry->getField("key_field"); + valueFiled = entry->getField("value_field"); + } + else + { + PRECOMPILED_LOG(ERROR) << LOG_BADGE("CRUDPrecompiled") << LOG_DESC("table not exist") + << LOG_KV("tableName", tableName); + } + callResult->setExecResult(abi.abiIn("", keyField, valueFiled)); + return callResult; + } + else if (func == name2Selector[CRUD_METHOD_INSERT_STR]) + {} +} +``` + ++ EntriesPrecompiled.cpp + +``` +/** +{ + "846719e0": "get(int256)", + "949d225d": "size()" +} +Table[{主key,Entries[]},{主key,Entries[]}]; +*/ +PrecompiledExecResult::Ptr EntriesPrecompiled::call( + ExecutiveContext::Ptr context, bytesConstRef param, Address const&, Address const&) +{ + uint32_t func = getParamFunc(param); + bytesConstRef data = getParamData(param); + + dev::eth::ContractABI abi; + + auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); + callResult->gasPricer()->setMemUsed(param.size()); + + if (func == name2Selector[ENTRIES_GET_INT]) + { // get(int256) + u256 num; + abi.abiOut(data, num); + + Entry::Ptr entry = getEntries()->get(num.convert_to()); + EntryPrecompiled::Ptr entryPrecompiled = std::make_shared(); + entryPrecompiled->setEntry(entry); + Address address = context->registerPrecompiled(entryPrecompiled); + callResult->setExecResult(abi.abiIn("", address)); + } + else if (func == name2Selector[ENTRIES_SIZE]) + { // size() + u256 c = getEntries()->size(); + callResult->setExecResult(abi.abiIn("", c)); + } + else + { + STORAGE_LOG(ERROR) << LOG_BADGE("EntriesPrecompiled") + << LOG_DESC("call undefined function!"); + } + return callResult; +} +``` ++ EntryPrecompiled.cpp +``` +/** +{ + "fda69fae": "getInt(string)", + "d52decd4": "getBytes64(string)", + "27314f79": "getBytes32(string)", + "bf40fac1": "getAddress(string)" + "2ef8ba74": "set(string,int256)", + "e942b516": "set(string,string)", +} +针对表中 某行某个字段的处理 +*/ +PrecompiledExecResult::Ptr EntryPrecompiled::call( + std::shared_ptr, bytesConstRef param, Address const&, Address const&) +{ + uint32_t func = getParamFunc(param); + bytesConstRef data = getParamData(param); + + dev::eth::ContractABI abi; + auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); + callResult->gasPricer()->setMemUsed(param.size()); + + if (func == name2Selector[ENTRY_GET_INT]) + { // getInt(string) + std::string str; + abi.abiOut(data, str); + s256 num = boost::lexical_cast(m_entry->getField(str)); + callResult->gasPricer()->appendOperation(InterfaceOpcode::GetInt); + callResult->setExecResult(abi.abiIn("", num)); + } + else if (func == name2Selector[ENTRY_GET_UINT]) + { // getUInt(string) + std::string str; + abi.abiOut(data, str); + u256 num = boost::lexical_cast(m_entry->getField(str)); + callResult->gasPricer()->appendOperation(InterfaceOpcode::GetInt); + callResult->setExecResult(abi.abiIn("", num)); + } + else if (func == name2Selector[ENTRY_SET_STR_INT]) + { // set(string,int256) + std::string key; + std::string value(setInt(data, key)); + m_entry->setField(key, value); + callResult->gasPricer()->appendOperation(InterfaceOpcode::Set); + } + else if (func == name2Selector[ENTRY_SET_STR_UINT]) + { // set(string,uint256) + std::string key; + std::string value(setInt(data, key, true)); + m_entry->setField(key, value); + callResult->gasPricer()->appendOperation(InterfaceOpcode::Set); + } + else if (func == name2Selector[ENTRY_SET_STR_STR]) + { // set(string,string) + std::string str; + std::string value; + abi.abiOut(data, str, value); + + m_entry->setField(str, value); + callResult->gasPricer()->appendOperation(InterfaceOpcode::Set); + } + else if (func == name2Selector[ENTRY_SET_STR_ADDR]) + { // set(string,address) + std::string str; + Address value; + abi.abiOut(data, str, value); + + m_entry->setField(str, toHex(value)); + callResult->gasPricer()->appendOperation(InterfaceOpcode::Set); + } + else if (func == name2Selector[ENTRY_GETA_STR]) + { // getAddress(string) + std::string str; + abi.abiOut(data, str); + + std::string value = m_entry->getField(str); + Address ret = Address(value); + callResult->setExecResult(abi.abiIn("", ret)); + callResult->gasPricer()->appendOperation(InterfaceOpcode::GetAddr); + } + else if (func == name2Selector[ENTRY_GETB_STR]) + { // getBytes64(string) + std::string str; + abi.abiOut(data, str); + + std::string value = m_entry->getField(str); + + string32 ret0; + string32 ret1; + + for (unsigned i = 0; i < 32; ++i) + ret0[i] = (i < value.size() ? value[i] : 0); + + for (unsigned i = 32; i < 64; ++i) + ret1[i - 32] = (i < value.size() ? value[i] : 0); + callResult->setExecResult(abi.abiIn("", ret0, ret1)); + callResult->gasPricer()->appendOperation(InterfaceOpcode::GetByte64); + } + else if (func == name2Selector[ENTRY_GETB_STR32]) + { // getBytes32(string) + std::string str; + abi.abiOut(data, str); + + std::string value = m_entry->getField(str); + dev::string32 s32 = dev::eth::toString32(value); + callResult->setExecResult(abi.abiIn("", s32)); + callResult->gasPricer()->appendOperation(InterfaceOpcode::GetByte32); + } + else if (func == name2Selector[ENTRY_GET_STR]) + { // getString(string) + std::string str; + abi.abiOut(data, str); + + std::string value = m_entry->getField(str); + callResult->setExecResult(abi.abiIn("", value)); + callResult->gasPricer()->appendOperation(InterfaceOpcode::GetString); + } + else + { + STORAGE_LOG(ERROR) << LOG_BADGE("EntryPrecompiled") << LOG_DESC("call undefined function!"); + } + return callResult; +} +``` + ++ KVTableFactoryPrecompiled.cpp +``` +/** +function openTable(string) public constant returns (KVTable); +function createTable(string, string, string) public returns (bool,int); + +*/ +PrecompiledExecResult::Ptr KVTableFactoryPrecompiled::call(ExecutiveContext::Ptr context, + bytesConstRef param, Address const& origin, Address const& sender) +{ + uint32_t func = getParamFunc(param); + bytesConstRef data = getParamData(param); + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("KVTableFactory") << LOG_DESC("call") + << LOG_KV("func", func); + + dev::eth::ContractABI abi; + auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); + callResult->gasPricer()->setMemUsed(param.size()); + + if (func == name2Selector[KVTABLE_FACTORY_METHOD_OPEN_TABLE]) + { // openTable(string) + string tableName; + abi.abiOut(data, tableName); + tableName = precompiled::getTableName(tableName); + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("KVTableFactory") << LOG_KV("openTable", tableName); + Address address; + auto table = m_memoryTableFactory->openTable(tableName); + callResult->gasPricer()->appendOperation(InterfaceOpcode::OpenTable); + if (table) + { + auto kvTablePrecompiled = make_shared(); + kvTablePrecompiled->setTable(table); + address = context->registerPrecompiled(kvTablePrecompiled); + } + else + { + PRECOMPILED_LOG(WARNING) + << LOG_BADGE("KVTableFactoryPrecompiled") << LOG_DESC("Open new table failed") + << LOG_KV("table name", tableName); + BOOST_THROW_EXCEPTION(PrecompiledException(tableName + " does not exist")); + } + callResult->setExecResult(abi.abiIn("", address)); + } + else if (func == name2Selector[KVTABLE_FACTORY_METHOD_CREATE_TABLE]){ + +} + +} +``` + ++ KVTablePrecompiled.cpp +``` +/** +function get(string) public constant returns(bool, Entry); +function set(string, Entry) public returns(bool, int); +function newEntry() public constant returns(Entry); +*/ +PrecompiledExecResult::Ptr KVTablePrecompiled::call(ExecutiveContext::Ptr context, + bytesConstRef param, Address const& origin, Address const& sender) +{ + uint32_t func = getParamFunc(param); + bytesConstRef data = getParamData(param); + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("KVTable") << LOG_DESC("call") << LOG_KV("func", func); + dev::eth::ContractABI abi; + + auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); + + callResult->gasPricer()->setMemUsed(param.size()); + + if (func == name2Selector[KVTABLE_METHOD_GET]) + { // get(string) + std::string key; + abi.abiOut(data, key); + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("KVTable") << LOG_KV("get", key); + + auto entries = m_table->select(key, m_table->newCondition()); + + callResult->gasPricer()->updateMemUsed(getEntriesCapacity(entries)); + callResult->gasPricer()->appendOperation(InterfaceOpcode::Select, entries->size()); + + if (entries->size() == 0) + { + callResult->setExecResult(abi.abiIn("", false, Address())); + } + else + { + auto entryPrecompiled = std::make_shared(); + // CachedStorage return entry use copy from + entryPrecompiled->setEntry( + std::const_pointer_cast(entries)->get(0)); + auto newAddress = context->registerPrecompiled(entryPrecompiled); + callResult->setExecResult(abi.abiIn("", true, newAddress)); + } + } + else if (func == name2Selector[KVTABLE_METHOD_SET]) + { // set(string,address) + if (!checkAuthority(context, origin, sender)) + { + PRECOMPILED_LOG(ERROR) + << LOG_BADGE("TablePrecompiled") << LOG_DESC("permission denied") + << LOG_KV("origin", origin.hex()) << LOG_KV("contract", sender.hex()); + BOOST_THROW_EXCEPTION(PrecompiledException( + "Permission denied. " + origin.hex() + " can't call contract " + sender.hex())); + } + std::string key; + Address entryAddress; + abi.abiOut(data, key, entryAddress); + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("KVTable") << LOG_KV("set", key); + EntryPrecompiled::Ptr entryPrecompiled = + std::dynamic_pointer_cast(context->getPrecompiled(entryAddress)); + auto entry = entryPrecompiled->getEntry(); + checkLengthValidate( + key, USER_TABLE_KEY_VALUE_MAX_LENGTH, CODE_TABLE_KEYVALUE_LENGTH_OVERFLOW, false); + + auto it = entry->begin(); + for (; it != entry->end(); ++it) + { + checkLengthValidate(it->second, USER_TABLE_FIELD_VALUE_MAX_LENGTH, + CODE_TABLE_KEYVALUE_LENGTH_OVERFLOW, false); + } + auto entries = m_table->select(key, m_table->newCondition()); + + callResult->gasPricer()->updateMemUsed(getEntriesCapacity(entries)); + callResult->gasPricer()->appendOperation(InterfaceOpcode::Select, entries->size()); + + int count = 0; + if (entries->size() == 0) + { + count = m_table->insert(key, entry, std::make_shared(origin)); + if (count > 0) + { + callResult->gasPricer()->setMemUsed(entry->capacity() * count); + callResult->gasPricer()->appendOperation(InterfaceOpcode::Insert, count); + } + } + else + { + count = m_table->update( + key, entry, m_table->newCondition(), std::make_shared(origin)); + if (count > 0) + { + callResult->gasPricer()->setMemUsed(entry->capacity() * count); + callResult->gasPricer()->appendOperation(InterfaceOpcode::Update, count); + } + } + if (count == storage::CODE_NO_AUTHORIZED) + { + BOOST_THROW_EXCEPTION( + PrecompiledException("Permission denied. " + origin.hex() + " can't write " + + m_table->tableInfo()->name)); + } + callResult->setExecResult(abi.abiIn("", s256(count))); + } + else if (func == name2Selector[KVTABLE_METHOD_NEWENT]) + { // newEntry() + auto entry = m_table->newEntry(); + entry->setForce(true); + auto entryPrecompiled = std::make_shared(); + entryPrecompiled->setEntry(entry); + + auto newAddress = context->registerPrecompiled(entryPrecompiled); + callResult->setExecResult(abi.abiIn("", newAddress)); + } + else + { + PRECOMPILED_LOG(ERROR) << LOG_BADGE("KVTablePrecompiled") + << LOG_DESC("call undefined function!"); + } + return callResult; +} +``` + ++ ParallelConfigPrecompiled.cpp +``` +//并行配置预编译合约 +PrecompiledExecResult::Ptr ParallelConfigPrecompiled::call( + dev::blockverifier::ExecutiveContext::Ptr context, bytesConstRef param, Address const& origin, + Address const&) +{ + // parse function name + uint32_t func = getParamFunc(param); + bytesConstRef data = getParamData(param); + + ContractABI abi; + auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); + + if (func == name2Selector[PARA_CONFIG_REGISTER_METHOD_ADDR_STR_UINT]) + { + registerParallelFunction(context, data, origin, callResult->mutableExecResult()); + } + else if (func == name2Selector[PARA_CONFIG_UNREGISTER_METHOD_ADDR_STR]) + { + unregisterParallelFunction(context, data, origin, callResult->mutableExecResult()); + } + else + { + PRECOMPILED_LOG(ERROR) << LOG_BADGE("ParallelConfigPrecompiled") + << LOG_DESC("call undefined function") << LOG_KV("func", func); + } + return callResult; +} +``` + ++ PermissionPrecompiled.cpp +``` +//权限预编译合约处理 +PrecompiledExecResult::Ptr PermissionPrecompiled::call( + ExecutiveContext::Ptr context, bytesConstRef param, Address const& origin, Address const&) +{ + // parse function name + uint32_t func = getParamFunc(param); + bytesConstRef data = getParamData(param); + + dev::eth::ContractABI abi; + auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); + int result = 0; + if (func == name2Selector[AUP_METHOD_INS]) + { // FIXME: modify insert(string,string) ==> insert(string,address) + // insert(string tableName,string addr) + }else if (func == name2Selector[AUP_METHOD_REM]) + { // remove(string tableName,string addr) + }else if (func == name2Selector[AUP_METHOD_QUE]) + { // queryByName(string table_name) + } else if (func == name2Selector[AUP_METHOD_GRANT_WRITE_CONTRACT]) + { // grantWrite(address,address) + }else if (func == name2Selector[AUP_METHOD_REVOKE_WRITE_CONTRACT]) + { // revokeWrite(address,address) + } else if (func == name2Selector[AUP_METHOD_QUERY_CONTRACT]) + { // queryPermission(address) + }else + {} +} +``` + ++ Precompiled.cpp +``` +std::map name2Selector; +// global function selector cache +static tbb::concurrent_unordered_map s_name2SelectCache; + +uint32_t Precompiled::getFuncSelector(std::string const& _functionName) +{ + // global function selector cache + if (s_name2SelectCache.count(_functionName)) + { + return s_name2SelectCache[_functionName]; + } + auto selector = getFuncSelectorByFunctionName(_functionName); + s_name2SelectCache.insert(std::make_pair(_functionName, selector)); + return selector; +} +``` + ++ PrecompiledGas.cpp +``` +// Traverse m_operationList to calculate total gas cost +u256 PrecompiledGas::calComputationGas() +{ + u256 totalGas = 0; + for (auto const& it : *m_operationList) + { + if (!m_metric->OpCode2GasCost.count(it.first)) + { + PrecompiledGas_LOG(WARNING) << LOG_DESC("Invalid opType:") << it.first; + continue; + } + totalGas += ((m_metric->OpCode2GasCost)[it.first]) * it.second; + } + return totalGas; +} + +// Calculating gas consumed by memory +u256 PrecompiledGas::calMemGas() +{ + if (m_memUsed == 0) + { + return 0; + } + auto memSize = (m_memUsed + GasMetrics::MemUnitSize - 1) / GasMetrics::MemUnitSize; + return (GasMetrics::MemGas * memSize) + (memSize * memSize) / 512; +} + +``` + ++ SystemConfigPrecompiled.cpp +``` +//系统配置预编译合约,操作SYS_CONFIG表 +PrecompiledExecResult::Ptr SystemConfigPrecompiled::call( + ExecutiveContext::Ptr context, bytesConstRef param, Address const& origin, Address const&) +{ + // parse function name + uint32_t func = getParamFunc(param); + bytesConstRef data = getParamData(param); + + dev::eth::ContractABI abi; + auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); + int count = 0; + int result = 0; + if (func == name2Selector[SYSCONFIG_METHOD_SET_STR]) + { + // setValueByKey(string,string) + std::string configKey, configValue; + abi.abiOut(data, configKey, configValue); + // Uniform lowercase configKey + boost::to_lower(configKey); + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("SystemConfigPrecompiled") + << LOG_DESC("setValueByKey func") << LOG_KV("configKey", configKey) + << LOG_KV("configValue", configValue); + + if (!checkValueValid(configKey, configValue)) + { + PRECOMPILED_LOG(DEBUG) + << LOG_BADGE("SystemConfigPrecompiled") << LOG_DESC("set invalid value") + << LOG_KV("configKey", configKey) << LOG_KV("configValue", configValue); + getErrorCodeOut(callResult->mutableExecResult(), CODE_INVALID_CONFIGURATION_VALUES); + return callResult; + } + + storage::Table::Ptr table = openTable(context, SYS_CONFIG); + + auto condition = table->newCondition(); + auto entries = table->select(configKey, condition); + auto entry = table->newEntry(); + entry->setField(SYSTEM_CONFIG_KEY, configKey); + entry->setField(SYSTEM_CONFIG_VALUE, configValue); + entry->setField(SYSTEM_CONFIG_ENABLENUM, + boost::lexical_cast(context->blockInfo().number + 1)); + + if (entries->size() == 0u) + { + count = table->insert(configKey, entry, std::make_shared(origin)); + if (count == storage::CODE_NO_AUTHORIZED) + { + PRECOMPILED_LOG(DEBUG) + << LOG_BADGE("SystemConfigPrecompiled") << LOG_DESC("permission denied"); + result = storage::CODE_NO_AUTHORIZED; + } + else + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("SystemConfigPrecompiled") + << LOG_DESC("setValueByKey successfully"); + result = count; + } + } + else + { + count = + table->update(configKey, entry, condition, std::make_shared(origin)); + if (count == storage::CODE_NO_AUTHORIZED) + { + PRECOMPILED_LOG(DEBUG) + << LOG_BADGE("SystemConfigPrecompiled") << LOG_DESC("permission denied"); + result = storage::CODE_NO_AUTHORIZED; + } + else + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("SystemConfigPrecompiled") + << LOG_DESC("update value by key successfully"); + result = count; + } + } + } + else + { + PRECOMPILED_LOG(ERROR) << LOG_BADGE("SystemConfigPrecompiled") + << LOG_DESC("call undefined function") << LOG_KV("func", func); + } + getErrorCodeOut(callResult->mutableExecResult(), result); + return callResult; +} + +``` + ++ TableFactoryPrecompiled.cpp +``` +PrecompiledExecResult::Ptr TableFactoryPrecompiled::call(ExecutiveContext::Ptr context, + bytesConstRef param, Address const& origin, Address const& sender) +{ + uint32_t func = getParamFunc(param); + bytesConstRef data = getParamData(param); + + dev::eth::ContractABI abi; + auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); + callResult->gasPricer()->setMemUsed(param.size()); + + if (func == name2Selector[TABLE_METHOD_OPT_STR]) + { // openTable(string) + } else if (func == name2Selector[TABLE_METHOD_CRT_STR_STR]) + { // createTable(string,string,string) + } +} +``` + ++ TablePrecompiled.cpp +``` +/** +{ + "31afac36": "insert(string,address)", + "7857d7c9": "newCondition()", + "13db9346": "newEntry()", + "28bb2117": "remove(string,address)", + "e8434e39": "select(string,address)", + "bf2b70a1": "update(string,address,address)" +} + +*/ +PrecompiledExecResult::Ptr TablePrecompiled::call(ExecutiveContext::Ptr context, + bytesConstRef param, Address const& origin, Address const& sender) +{ + uint32_t func = getParamFunc(param); + bytesConstRef data = getParamData(param); + + dev::eth::ContractABI abi; + auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); + callResult->gasPricer()->setMemUsed(param.size()); + + if (func == name2Selector[TABLE_METHOD_SLT_STR_ADD]) + { // select(string,address) + } else if (func == name2Selector[TABLE_METHOD_INS_STR_ADD]) + { // insert(string,address) + } else if (func == name2Selector[TABLE_METHOD_NEWCOND]) + { // newCondition() + } else if (func == name2Selector[TABLE_METHOD_NEWENT]) + { // newEntry() + } else if (func == name2Selector[TABLE_METHOD_RE_STR_ADD]) + { // remove(string,address) + } else if (func == name2Selector[TABLE_METHOD_UP_STR_2ADD]) + { // update(string,address,address) + } +} +``` + ++ WorkingSealerManagerPrecompiled.cpp +``` +//rotateWorkingSealer()-->WorkingSealerManagerPrecompiled::rotateWorkingSealer()-->WorkingSealerManagerImpl::rotateWorkingSealer() + +PrecompiledExecResult::Ptr WorkingSealerManagerPrecompiled::call( + std::shared_ptr _context, bytesConstRef _param, + Address const& _origin, Address const& _sender) +{ + // get function selector + auto funcSelector = getParamFunc(_param); + auto paramData = getParamData(_param); + dev::eth::ContractABI abi; + auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); + // Call the corresponding function according to the selector + if (funcSelector == name2Selector[WSM_METHOD_ROTATE_STR]) + { + rotateWorkingSealer(_context, paramData, abi, _origin, _sender); + } + return callResult; +} +``` + ++ WorkingSealerManagerImpl.cpp +``` +bool VRFInfo::verifyProof() +{ + return ( + curve25519_vrf_verify(m_vrfPublicKey.c_str(), m_vrfInput.c_str(), m_vrfProof.c_str()) == 0); +} + +void WorkingSealerManagerImpl::rotateWorkingSealer() +{ + if (!shouldRotate()) + { + return; + } + try + { + // check VRFInfos firstly + checkVRFInfos(); + } + catch (PrecompiledException& e) + { + throw; + } + // a valid transaction, reset the INTERNAL_SYSTEM_KEY_NOTIFY_ROTATE flag + tryToResetNotifyNextLeaderFlag(); + int64_t sealersNum = m_workingSealerList->size() + m_pendingSealerList->size(); + if (sealersNum <= 1 || + (m_configuredEpochSealersSize == sealersNum && 0 == m_pendingSealerList->size())) + { + return; + } + // calculate the number of working sealers need to be removed and inserted + auto nodeRotatingInfo = calNodeRotatingInfo(); + // get hash through VRF proof + m_proofHash = m_vrfInfo->getHashFromProof(); + + if (nodeRotatingInfo->removedWorkingSealerNum > 0) + { + // select working sealers to be removed + // Note: Since m_workingSealerList will not be used afterwards, + // after updating the node type, it is not updated + auto workingSealersToRemove = + selectNodesFromList(m_workingSealerList, nodeRotatingInfo->removedWorkingSealerNum); + // update node type from workingSealer to sealer + for (auto const& node : *workingSealersToRemove) + { + UpdateNodeType(node, NODE_TYPE_SEALER); + } + } + + if (nodeRotatingInfo->insertedWorkingSealerNum > 0) + { + + // select working sealers to be inserted + auto workingSealersToInsert = + selectNodesFromList(m_pendingSealerList, nodeRotatingInfo->insertedWorkingSealerNum); + // Note: Since m_pendingSealerList will not be used afterwards, + // after updating the node type, it is not updated + for (auto const& node : *workingSealersToInsert) + { + UpdateNodeType(node, NODE_TYPE_WORKING_SEALER); + } + } +} + +``` + ++ CryptoPrecompiled.cpp +``` +/**涉及加密的四个函数sm3()、keccak256Hash()、sm2Verify()、curve25519VRFVerify() +*/ +PrecompiledExecResult::Ptr CryptoPrecompiled::call( + std::shared_ptr, bytesConstRef _param, Address const&, Address const&) +{ + auto funcSelector = getParamFunc(_param); + auto paramData = getParamData(_param); + ContractABI abi; + auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); + callResult->gasPricer()->setMemUsed(_param.size()); + do + { + if (funcSelector == name2Selector[CRYPTO_METHOD_SM3_STR]) + { + bytes inputData; + abi.abiOut(paramData, inputData); + auto sm3Hash = sm3(inputData); + PRECOMPILED_LOG(TRACE) + << LOG_DESC("CryptoPrecompiled: sm3") << LOG_KV("input", toHex(inputData)) + << LOG_KV("result", toHex(sm3Hash)); + callResult->setExecResult(abi.abiIn("", dev::eth::toString32(sm3Hash))); + break; + } + if (funcSelector == name2Selector[CRYPTO_METHOD_KECCAK256_STR]) + { + bytes inputData; + abi.abiOut(paramData, inputData); + auto keccak256Hash = keccak256(inputData); + PRECOMPILED_LOG(TRACE) + << LOG_DESC("CryptoPrecompiled: keccak256") << LOG_KV("input", toHex(inputData)) + << LOG_KV("result", toHex(keccak256Hash)); + callResult->setExecResult(abi.abiIn("", dev::eth::toString32(keccak256Hash))); + break; + } + if (funcSelector == name2Selector[CRYPTO_METHOD_SM2_VERIFY_STR]) + { + sm2Verify(paramData, callResult); + break; + } + if (funcSelector == name2Selector[CRYPTO_METHOD_CURVE25519_VRF_VERIFY_STR]) + { + curve25519VRFVerify(paramData, callResult); + break; + } + // no defined function + callResult->setExecResult(abi.abiIn("", u256((uint32_t)CODE_UNKNOW_FUNCTION_CALL))); + } while (0); + return callResult; +} +``` + ++ DagTransferPrecompiled.cpp +``` +PrecompiledExecResult::Ptr DagTransferPrecompiled::call( + dev::blockverifier::ExecutiveContext::Ptr context, bytesConstRef param, Address const& origin, + Address const&) +{ + // PRECOMPILED_LOG(TRACE) << LOG_BADGE("DagTransferPrecompiled") << LOG_DESC("call") + // << LOG_KV("param", toHex(param)); + + // parse function name + uint32_t func = getParamFunc(param); + bytesConstRef data = getParamData(param); + auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); + // user_name user_balance 2 fields in table, the key of table is user_name field + if (func == name2Selector[DAG_TRANSFER_METHOD_ADD_STR_UINT]) + { // userAdd(string,uint256) + userAddCall(context, data, origin, callResult->mutableExecResult()); + } + else if (func == name2Selector[DAG_TRANSFER_METHOD_SAV_STR_UINT]) + { // userSave(string,uint256) + userSaveCall(context, data, origin, callResult->mutableExecResult()); + } + else if (func == name2Selector[DAG_TRANSFER_METHOD_DRAW_STR_UINT]) + { // userDraw(string,uint256) + userDrawCall(context, data, origin, callResult->mutableExecResult()); + } + else if (func == name2Selector[DAG_TRANSFER_METHOD_TRS_STR2_UINT]) + { // userTransfer(string,string,uint256) + userTransferCall(context, data, origin, callResult->mutableExecResult()); + } + else if (func == name2Selector[DAG_TRANSFER_METHOD_BAL_STR]) + { // userBalance(string user) + userBalanceCall(context, data, origin, callResult->mutableExecResult()); + } + else + { + // PRECOMPILED_LOG(ERROR) << LOG_BADGE("DagTransferPrecompiled") << LOG_DESC("error func") + // << LOG_KV("func", func); + } + + // PRECOMPILED_LOG(TRACE) << LOG_BADGE("DagTransferPrecompiled") << LOG_DESC("call") + // << LOG_DESC("end"); + + return callResult; +} +``` + ++ GroupSigPrecompiled.cpp +``` +//群签名预编译合约 +PrecompiledExecResult::Ptr GroupSigPrecompiled::call( + ExecutiveContext::Ptr, bytesConstRef param, Address const&, Address const&) +{ + PRECOMPILED_LOG(TRACE) << LOG_BADGE("GroupSigPrecompiled") << LOG_DESC("call") + << LOG_KV("param", toHex(param)); + + // parse function name + uint32_t func = getParamFunc(param); + bytesConstRef data = getParamData(param); + + dev::eth::ContractABI abi; + auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); + + callResult->gasPricer()->setMemUsed(param.size()); + if (func == name2Selector[GroupSig_METHOD_SET_STR]) + { + // groupSigVerify(string) + std::string signature, message, gpkInfo, paramInfo; + abi.abiOut(data, signature, message, gpkInfo, paramInfo); + bool result = false; + + try + { + result = GroupSigApi::group_verify(signature, message, gpkInfo, paramInfo); + callResult->gasPricer()->appendOperation(InterfaceOpcode::GroupSigVerify); + } + catch (std::string& errorMsg) + { + PRECOMPILED_LOG(ERROR) << LOG_BADGE("GroupSigPrecompiled") << LOG_DESC(errorMsg) + << LOG_KV("signature", signature) << LOG_KV("message", message) + << LOG_KV("gpkInfo", gpkInfo) << LOG_KV("paramInfo", paramInfo); + getErrorCodeOut(callResult->mutableExecResult(), VERIFY_GROUP_SIG_FAILED); + return callResult; + } + callResult->setExecResult(abi.abiIn("", result)); + } + else + { + PRECOMPILED_LOG(ERROR) << LOG_BADGE("GroupSigPrecompiled") + << LOG_DESC("call undefined function") << LOG_KV("func", func); + getErrorCodeOut(callResult->mutableExecResult(), CODE_UNKNOW_FUNCTION_CALL); + } + return callResult; +} + +``` + ++ PaillierPrecompiled.cpp +``` +//Paillier是一个支持加法同态的公钥密码系统 +PrecompiledExecResult::Ptr PaillierPrecompiled::call( + ExecutiveContext::Ptr, bytesConstRef param, Address const&, Address const&) +{ + PRECOMPILED_LOG(TRACE) << LOG_BADGE("PaillierPrecompiled") << LOG_DESC("call") + << LOG_KV("param", toHex(param)); + + // parse function name + uint32_t func = getParamFunc(param); + bytesConstRef data = getParamData(param); + + dev::eth::ContractABI abi; + auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); + callResult->gasPricer()->setMemUsed(param.size()); + if (func == name2Selector[PAILLIER_METHOD_SET_STR]) + { + // paillierAdd(string,string) + std::string cipher1, cipher2; + abi.abiOut(data, cipher1, cipher2); + std::string result; + try + { + result = m_callPaillier->paillierAdd(cipher1, cipher2); + callResult->gasPricer()->appendOperation(InterfaceOpcode::PaillierAdd); + } + catch (CallException& e) + { + PRECOMPILED_LOG(ERROR) + << LOG_BADGE("PaillierPrecompiled") << LOG_DESC(std::string(e.what())) + << LOG_KV("cipher1", cipher1) << LOG_KV("cipher2", cipher2); + getErrorCodeOut(callResult->mutableExecResult(), CODE_INVALID_CIPHERS); + return callResult; + } + callResult->setExecResult(abi.abiIn("", result)); + } + else + { + PRECOMPILED_LOG(ERROR) << LOG_BADGE("PaillierPrecompiled") + << LOG_DESC("call undefined function") << LOG_KV("func", func); + getErrorCodeOut(callResult->mutableExecResult(), CODE_UNKNOW_FUNCTION_CALL); + } + return callResult; +} +``` + ++ RingSigPrecompiled.cpp +``` +//环签名预编译合约 +PrecompiledExecResult::Ptr RingSigPrecompiled::call( + ExecutiveContext::Ptr, bytesConstRef param, Address const&, Address const&) +{ + PRECOMPILED_LOG(TRACE) << LOG_BADGE("RingSigPrecompiled") << LOG_DESC("call") + << LOG_KV("param", toHex(param)); + + // parse function name + uint32_t func = getParamFunc(param); + bytesConstRef data = getParamData(param); + + dev::eth::ContractABI abi; + auto callResult = m_precompiledExecResultFactory->createPrecompiledResult(); + callResult->gasPricer()->setMemUsed(param.size()); + + if (func == name2Selector[RingSig_METHOD_SET_STR]) + { + // ringSigVerify(string,string,string) + std::string signature, message, paramInfo; + abi.abiOut(data, signature, message, paramInfo); + bool result = false; + + try + { + result = RingSigApi::LinkableRingSig::ring_verify(signature, message, paramInfo); + callResult->gasPricer()->appendOperation(InterfaceOpcode::RingSigVerify); + } + catch (std::string& errorMsg) + { + PRECOMPILED_LOG(ERROR) << LOG_BADGE("RingSigPrecompiled") << LOG_DESC(errorMsg) + << LOG_KV("signature", signature) << LOG_KV("message", message) + << LOG_KV("paramInfo", paramInfo); + getErrorCodeOut(callResult->mutableExecResult(), VERIFY_RING_SIG_FAILED); + return callResult; + } + callResult->setExecResult(abi.abiIn("", result)); + } + else + { + PRECOMPILED_LOG(ERROR) << LOG_BADGE("RingSigPrecompiled") + << LOG_DESC("call undefined function") << LOG_KV("func", func); + getErrorCodeOut(callResult->mutableExecResult(), CODE_UNKNOW_FUNCTION_CALL); + } + return callResult; +} +``` +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ + diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/librpc.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/librpc.md new file mode 100644 index 000000000..8f0ebbafa --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/librpc.md @@ -0,0 +1,55 @@ +# librpc 模块 +作者:TrustChain [微信公众号] + +librpc:客户端(如区块链浏览器)与区块链系统交互的一套协议和接口。 + + + +## 主要内容有: + ++ 用户客户端通过RPC接口连接节点可查询区块链相关信息(如块高、区块、节点连接等)、发送交易;接受JSON-RPC格式的请求。 + +![](../../../../images/articles/sourceCode_knowledge_map/librpc.png) + +## 涉及知识点: + ++ JsonHelper.cpp:从json解析出交易数据结构的字段; + ++ StatisticProtocolServer.cpp:继承jsonrpc::RpcProtocolServerV2,m_groupRPCMethodSet存放可供客户端RPC调用的函数; + ++ SafeHttpServer.cpp:继承jsonrpc::AbstractServerConnector,处理httpt json-rpc请求处理;支持POST、OPTIONS; + ++ Rpc.cpp:实现JSON-RPC各个函数,供客户端调用; +``` +//发送交易的rpc函数 +std::string Rpc::sendRawTransaction(int _groupID, const std::string& _rlp, + std::function( + std::weak_ptr _blockChain, + LocalisedTransactionReceipt::Ptr receipt, dev::bytesConstRef input, + dev::eth::Block::Ptr _blockPtr)> + _notifyCallback) +{ +// Note: Since blockChain has a transaction cache, that is, +// BlockChain holds transactions, in order to prevent circular references, +// the callback of the transaction cannot hold the blockChain of shared_ptr, +// must be weak_ptr +tx->setRpcCallback( +[weakedBlockChain, _notifyCallback, transactionCallback, clientProtocolversion, + _groupID](LocalisedTransactionReceipt::Ptr receipt, dev::bytesConstRef input, + dev::eth::Block::Ptr _blockPtr) { +} + +//case ProtocolVersion::v1: +//case ProtocolVersion::v2: +ret = txPool->submitTransactions(tx); + +//case ProtocolVersion::v3: +ret = txPool->submit(tx); + +} +``` +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ \ No newline at end of file diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libsecurity.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libsecurity.md new file mode 100644 index 000000000..5ae0c4e02 --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libsecurity.md @@ -0,0 +1,69 @@ +# libsecurity 模块 +作者:TrustChain [微信公众号] + +libconfig:配置文件。 + +## 主要内容有: + ++ 落盘加密:cipherDataKey、dataKey、superKey。 + ++ 节点用自己的dataKey,对自身加密的数据(Encrypted Space)进行加解密。 + ++ 节点本身不会在本地磁盘中存储dataKey,而是存储dataKey被加密后的cipherDataKey。 + ++ 节点启动时,拿cipherDataKey向Key Manager请求,获取dataKey。Key Manager持有全局的superKey,负责对所有节点启动时的授权请求进行响应,授权。 + ++ dataKey只在节点的内存中,当节点关闭后,dataKey自动丢弃。 + +![](../../../../images/articles/sourceCode_knowledge_map/libsecurity.png) + +## 涉及知识点: + ++ EncryptedFile.cpp +``` +//落盘文件内容解密--》dataKey +bytes EncryptedFile::decryptContents(const std::string& _filePath) +{ +string encContextsStr = contentsString(_filePath); +encFileBytes = fromHex(encContextsStr); +auto dataKey = g_BCOSConfig.diskEncryption.dataKey; +string decFileBytesBase64 = crypto::SymmetricDecrypt((const unsigned char*)encFileBytes.data(), encFileBytes.size(), (const unsigned char*)dataKey.data(), dataKey.size(), +(const unsigned char*)dataKey.substr(0, 16).data()); +decFileBytes = fromBase64(decFileBytesBase64); +} +``` + ++ EncryptedLevelDB.cpp +``` +//存放leveldb的value的内容加密 +leveldb::Status EncryptedLevelDB::Put( + const leveldb::WriteOptions& _options, const leveldb::Slice& _key, const leveldb::Slice& _value) +{ +enData = encryptValue(m_dataKey, _value); +m_db->Put(_options, _key, leveldb::Slice(enData)); +} +//通过cipherDataKey获取datakey +string EncryptedLevelDB::getKeyOfDatabase() +{ + std::string key; + leveldb::Status status = m_db->Get(leveldb::ReadOptions(), leveldb::Slice(c_cipherDataKeyName), &key); + return key; +} +``` + ++ KeyCenter.cpp +``` +//节点启动前需考虑是否落盘加密,一般不可变动状态,启动落盘加密时,会创建一个KeyCenterHttpClient连接部署在内网环境的Key Manager进行请求。一个keyCenter有一个superKey +Json::Value KeyCenterHttpClient::callMethod(const string& _method, Json::Value _params) +{ +/* + query is: +{"jsonrpc":"2.0","method":"encDataKey","params",["123456"],"id":83} +*/ +} +``` +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ \ No newline at end of file diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libstat.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libstat.md new file mode 100644 index 000000000..b3f5588ca --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libstat.md @@ -0,0 +1,60 @@ +# libstat 模块 + +作者:TrustChain [微信公众号] + +libstat:网络状态统计。 + +## 主要内容有: + ++ 负责P2P通信网络状态统计; + ++ 负责SDK通信网络状态统计。 + +![](../../../../images/articles/sourceCode_knowledge_map/libstat.png) + +## 涉及知识点: + ++ NetworkStatHandler.cpp +``` +//p2p:统计共识消息、区块同步消息的网络状态 +consType = dev::eth::ProtocolID::Raft; +consType = dev::eth::ProtocolID::PBFT; +m_p2pMsgTypeToDesc = {{consType, "CONS"}, {dev::eth::ProtocolID::BlockSync, "SYNC"}}; + +//SDK:c_sdkInMsgTypeToDesc +std::map const c_sdkInMsgTypeToDesc = { + {dev::channel::ChannelMessageType::CHANNEL_RPC_REQUEST, "RPC"}, + {dev::channel::ChannelMessageType::CLIENT_REGISTER_EVENT_LOG, "RegitsterEvent"}, {dev::channel::ChannelMessageType::CLIENT_UNREGISTER_EVENT_LOG, "UnregitsterEvent"}}; +//SDK:c_sdkOutMsgTypeToDesc +std::map const c_sdkOutMsgTypeToDesc = { + {dev::channel::ChannelMessageType::CHANNEL_RPC_REQUEST, "RPC"}, + {dev::channel::ChannelMessageType::TRANSACTION_NOTIFY, "Txs"}, + {dev::channel::ChannelMessageType::EVENT_LOG_PUSH, "EventLog"}}; + +//记录每种类型消息的大小之和 +std::shared_ptr> m_InMsgTypeToBytes; +std::shared_ptr> m_OutMsgTypeToBytes; +``` + ++ ChannelNetworkStatHandler.cpp + +``` +//每个群组有个p2p统计CHANNEL_RPC_REQUEST类型的输入输出包的大小 +std::shared_ptr> m_p2pStatHandlers; +p2pStatHandler->updateOutgoingTraffic(ChannelMessageType::CHANNEL_RPC_REQUEST, _msgSize); +p2pStatHandler->updateIncomingTraffic(ChannelMessageType::CHANNEL_RPC_REQUEST, _msgSize); + +//updateAMOPInTraffic、updateAMOPOutTraffic两个函数统计AMOP通信的消息大小 + +//统计响应消息的消息大小 +void ChannelNetworkStatHandler::updateGroupResponseTraffic( +p2pStatHandler->updateOutgoingTraffic(_msgType, _msgSize); + +} +``` + +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ \ No newline at end of file diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libstorage.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libstorage.md new file mode 100644 index 000000000..086f2ad06 --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libstorage.md @@ -0,0 +1,78 @@ +# libstorage 模块 +作者:TrustChain [微信公众号] + +libstorage:数据落盘存储模块。 + + + +## 主要内容有: + ++ 数据落盘存储模块,通过Storage和MemoryTable框架管理; + ++ CRUD数据、区块数据默认情况下都保存在AMDB,合约局部变量存储可根据需要配置为MPTState或StorageState,无论配置哪种State,合约代码都不需要变动; + ++ 当使用MPTState时,合约局部变量保存在MPT树中。当使用StorageState时,合约局部变量保存在AMDB表中; + ++ 尽管MPTState和AMDB最终数据都会写向RocksDB,但二者使用不同的RocksDB实例,没有事务性,因此当配置成使用MPTState时,提交数据时异常可能导致两个RocksDB数据不一致。 + +![](../../../../images/articles/sourceCode_knowledge_map/libstorage.png) + +## 涉及知识点: + ++ BasicRocksDB.cpp:操作数据,增删改查,有加密和压缩选项;一般是压缩后再加密;有BatchPut、PutWithLock两种提供性能的操作; + ++ BinaryLogStorage.cpp:写区块数据到binlog,有select查表、commit写区块数据到表中; + ++ BinLogHandler.cpp:writeBlocktoBinLog函数先把区块数据编码写入一个buffer,处理后再把buffer数据写入m_fd二进制文件;该文件内容有:length(4B)+block heigth+block data+CRC32; Entry:ID+vecField bitmap+ every Field value; + ++ CachedStorage.cpp:每一个groupID有专门的缓存:任务线程池+异步线程池+MRU队列; + ++ LevelDBStorage.cpp:有select查表、commit写区块数据到表中; + ++ MemoryTable2.cpp:TableData{key, entries},一般根据key值查询,有就在entries增加entry;TableInfo数据结构为表头元数据; + ++ MemoryTableFactory.cpp、MemoryTableFactory2.cpp:用于系统表、用户表的操作; + ++ ScalableStorage.cpp:主要是将区块数据或者区块state状态数据归档存储到s_block_to_dbname表中。 + ++ SQLBasicAccess.cpp:SQL操作、类似BuildQuerySql函数过滤一些字符,比如\,`等; + ++ SQLConnectionPool.cpp:目前仅支持mysql数据库类型的连接池; + ++ SQLStorage.cpp:通过json-rpc、channelMessage、AMOP接口进行sql操作数据库表; + ++ ZdbStorage.cpp:连接mysql数据库、重点是initSysTables函数; +``` + +void ZdbStorage::initSysTables() +{ + createSysTables(); + createSysConsensus(); + createAccessTables(); + createCurrentStateTables(); + createNumber2HashTables(); + createTxHash2BlockTables(); + + createCnsTables(); + createSysConfigTables(); + if (g_BCOSConfig.version() >= V2_6_0) + { + // the compressed table include: + // _sys_hash_2_block, _sys_block_2_nonce_ and _sys_hash_2_header_ + m_rowFormat = " ROW_FORMAT=COMPRESSED "; + m_valueFieldType = "longblob"; + } + createHash2BlockTables(); + createSysBlock2NoncesTables(); + + createBlobSysHash2BlockHeaderTable(); + insertSysTables(); +} + +``` + +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ \ No newline at end of file diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libstoragestate.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libstoragestate.md new file mode 100644 index 000000000..06245d1cd --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libstoragestate.md @@ -0,0 +1,62 @@ +# libstoragestate 模块 +作者:TrustChain [微信公众号] + +libstoragestate:当使用StorageState时,存储区块链的状态数据。 + +## 主要内容有: + ++ 账户数据存储方式:AMDB表,不存历史状态,性能更高;目前已经支持LevelDB、RocksDB和MySQL; + ++ Table[{主key,Entries[]},{主key,Entries[]}]; + ++ Entry示例: {Name:Alice,item_id:1001001,item_name:laptop}; + ++ 表分类:系统表、用户表、账户表。系统表默认存在,由存储驱动保证系统表的创建。用户表:用户调用CRUD接口所创建的表。StorageState账户表。 + + +![](../../../../images/articles/sourceCode_knowledge_map/libstoragestate.png) + +## 涉及知识点: ++ 数据结构 +``` + +#StorageState账户表的字段 +const char* const STORAGE_KEY = "key"; +const char* const STORAGE_VALUE = "value"; +const char* const ACCOUNT_BALANCE = "balance"; +const char* const ACCOUNT_CODE_HASH = "codeHash"; +const char* const ACCOUNT_CODE = "code"; +const char* const ACCOUNT_NONCE = "nonce"; +const char* const ACCOUNT_ALIVE = "alive"; +const char* const ACCOUNT_AUTHORITY = "authority"; +const char* const ACCOUNT_FROZEN = "frozen"; +``` ++ 转账操作:transferBalance函数。 +``` + +void StorageState::transferBalance(Address const& _from, Address const& _to, u256 const& _value) +{ + subBalance(_from, _value); + addBalance(_to, _value); +} +``` + ++ 获取账户的状态根:storageRoot函数。 +``` + +h256 StorageState::storageRoot(Address const& _address) const +{ + auto table = getTable(_address); + if (table) + { + return table->hash(); + } + return h256(); +} + +``` +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ \ No newline at end of file diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libsync.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libsync.md new file mode 100644 index 000000000..5aef45b9f --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libsync.md @@ -0,0 +1,640 @@ +# libsync 模块 + +作者:TrustChain [微信公众号] + +libsync:区块、交易的同步逻辑,由SyncMaster管理。 + + +## 主要内容有: + ++ 区块/状态同步机制:区块状态树状广播、采用gossip协议定期同步区块状态策略; + ++ 交易同步机制:交易树状广播和交易转发时随机发送给25%的节点。 + +![](../../../../images/articles/sourceCode_knowledge_map/libsync.png) + +## 涉及知识点: + ++ Common.h + +``` +enum SyncPacketType : byte +{ + StatusPacket = 0x00, + TransactionsPacket = 0x01, + BlocksPacket = 0x02, + ReqBlocskPacket = 0x03, + TxsStatusPacket = 0x04, + TxsRequestPacekt = 0x05, + PacketCount +}; + +enum class SyncState +{ + Idle, ///< Initial chain sync complete. Waiting for new packets + Downloading, ///< Downloading blocks + Size /// Must be kept last +}; +``` ++ DownloadingBlockQueue.cpp + +``` +//重要数据结构 +std::priority_queue m_blocks; +std::shared_ptr m_buffer; // use buffer for faster push return + +//分片 +class DownloadBlocksShard +{ +public: + DownloadBlocksShard(int64_t _fromNumber, int64_t _size, bytes const& _blocksBytes) + : fromNumber(_fromNumber), size(_size), blocksBytes(_blocksBytes) + {} + int64_t fromNumber; + int64_t size; + bytes blocksBytes; +}; +using ShardPtr = std::shared_ptr; +using ShardPtrVec = std::list; +``` + +``` +//入队列 +void DownloadingBlockQueue::push(RLP const& _rlps) +{ +ShardPtr blocksShard = make_shared(0, 0, _rlps.data().toBytes()); +m_buffer->emplace_back(blocksShard); +} +//出队列 +void DownloadingBlockQueue::pop() +{ +//m_blockSizeExpandCoeff: the expand coeff of memory-size after block-decode +// Note: the memory size occupied by Block object will increase to at least treble +// for: +// 1. txsCache of Block +// 2. m_rlpBuffer of every Transaction +// 3. the Block occupied memory calculated without cache + +auto blockSize = m_blocks.top()->blockSize() * m_blockSizeExpandCoeff; +m_blockQueueSize -= blockSize; +m_blocks.pop(); +} +//清除队列 +void DownloadingBlockQueue::clearQueue() +{ + WriteGuard l(x_blocks); + std::priority_queue emptyQueue; + swap(m_blocks, emptyQueue); // Does memory leak here ? + // give back the memory to os +#ifndef FISCO_DEBUG + MallocExtension::instance()->ReleaseFreeMemory(); +#endif +} + +//从缓存m_buffer循环取区块分片blocksShard到区块队列m_blocks中 +void DownloadingBlockQueue::flushBufferToQueue() +{ + WriteGuard l(x_buffer); + bool ret = true; + while (m_buffer->size() > 0 && ret) + { + //返回list第一个元素的引用 + auto blocksShard = m_buffer->front(); + //删除list容器的第一个元素 + m_buffer->pop_front(); + ret = flushOneShard(blocksShard); + } +} +``` ++ DownloadingTxsQueue.cpp + +``` +//重要的数据结构 +//交易分片vector +std::shared_ptr>> m_buffer; +//节点树拓扑状传播路由 +TreeTopology::Ptr m_treeRouter = nullptr; +//交易分片类 +class DownloadTxsShard +{ +public: + DownloadTxsShard(bytesConstRef _txsBytes, NodeID const& _fromPeer) + : txsBytes(_txsBytes.toBytes()), fromPeer(_fromPeer) + { + knownNodes = std::make_shared(); + } + + void appendKnownNode(dev::h512 const& _knownNode) { knownNodes->push_back(_knownNode); } + bytes txsBytes; + NodeID fromPeer; + std::shared_ptr knownNodes; +}; +``` + +``` +void DownloadingTxsQueue::push( + SyncMsgPacket::Ptr _packet, dev::p2p::P2PMessage::Ptr _msg, NodeID const& _fromPeer) +{ +std::shared_ptr txsShard = + std::make_shared(_packet->rlp().data(), _fromPeer); + //_msg->packetType() == SyncPacketType的TransactionsPacket + int RPCPacketType = 1; + if (_msg->packetType() == RPCPacketType && m_treeRouter) + { + int64_t consIndex = _packet->rlp()[1].toPositiveInt64(); + SYNC_LOG(DEBUG) << LOG_DESC("receive and send transactions by tree") + << LOG_KV("consIndex", consIndex) + << LOG_KV("fromPeer", _fromPeer.abridged()); + //获取需要转发的节点ID + auto selectedNodeList = m_treeRouter->selectNodes(m_syncStatus->peersSet(), consIndex); + // append all the parent nodes into the knownNode + auto parentNodeList = m_treeRouter->selectParent(m_syncStatus->peersSet(), consIndex, true); + for (auto const& _parentNodeId : *parentNodeList) + { + txsShard->appendKnownNode(_parentNodeId); + } + // forward the received txs + for (auto const& selectedNode : *selectedNodeList) + { + //排除msg的来源节点fromPeer + if (selectedNode == _fromPeer || !m_service) + { + continue; + } + //异步发送p2pMessage + m_service->asyncSendMessageByNodeID(selectedNode, _msg, nullptr); + txsShard->appendKnownNode(selectedNode); + SYNC_LOG(DEBUG) << LOG_DESC("forward transaction") + << LOG_KV("selectedNode", selectedNode.abridged()); + } + } + WriteGuard l(x_buffer); + //存放交易分片 + m_buffer->emplace_back(txsShard); +} +``` + +``` +//把队列分片中的交易经过条件判断后提交交易池TxPool +void DownloadingTxsQueue::pop2TxPool( + std::shared_ptr _txPool, dev::eth::CheckTransaction _checkSig) +{ +// fetch from buffer(only one thread can callback this function) +// localBuffer指向待处理的m_buffer,然后m_buffer指向新的vector; std::shared_ptr>> localBuffer; +{ +localBuffer = m_buffer; +m_buffer = std::make_shared>>(); +} + for (size_t i = 0; i < localBuffer->size(); ++i) + { + auto txs = std::make_shared(); + std::shared_ptr txsShard = (*localBuffer)[i]; + RLP const& txsBytesRLP = RLP(ref(txsShard->txsBytes))[0]; + dev::eth::TxsParallelParser::decode( + txs, txsBytesRLP.toBytesConstRef(), _checkSig, true); + // parallel verify transaction before import + tbb::parallel_for(tbb::blocked_range(0, txs->size()),[&](const tbb::blocked_range& _r) {}); + //遍历交易 + for (auto tx : *txs) + { + //提交交易池 + auto importResult = _txPool->import(tx); + } + } +} +``` + ++ GossipBlockStatus.cpp + +``` +//重要的消息处理函数 +std::function m_gossipBlockStatusHandler = nullptr; +//继承Worker,启动线程处理 +``` + ++ NodeTimeMaintenance.cpp + +``` +//记录节点系统时间,并协调各节点系统时间尽量一致 +// maps between nodeID and the timeOffset +std::shared_ptr > m_node2TimeOffset; +std::atomic m_medianTimeOffset = {0}; +void NodeTimeMaintenance::tryToUpdatePeerTimeInfo(SyncStatusPacket::Ptr _peerStatus) +{ +} +``` + ++ SyncMsgPacket.cpp + +``` +//SyncMsgPacket:父类,重要数据结构 +RLP m_rlp; /// The result of decode +RLPStream m_rlpStream; // The result of encode + +//SyncStatusPacket:同步节点状态,encode时包括如下字段: +int64_t number; +h256 genesisHash; +h256 latestHash; + +//SyncStatusPacketWithAlignedTime:节点对齐时间,encode时包括如下字段: +int64_t number; +h256 genesisHash; +h256 latestHash; +int64_t alignedTime; + +//SyncTransactionsPacket:同步交易的包,encode时包括如下字段: +std::vector const& _txRLPs +uint64_t const& _consIndex + +//SyncBlocksPacket:同步区块的包,encode时每个区块为一个字段 +void SyncBlocksPacket::encode(std::vector const& _blockRLPs) +{ + m_rlpStream.clear(); + unsigned size = _blockRLPs.size(); + prep(m_rlpStream, BlocksPacket, size); + for (bytes const& bs : _blockRLPs) + m_rlpStream.append(bs); +} + +//SyncReqBlockPacket:请求区块的包,encode时包括如下字段: +int64_t _from +unsigned _size + +//SyncTxsStatusPacket:同步交易哈希的包,encode时包括如下字段: +int64_t const& _number +std::shared_ptr> _txsHash + +//SyncTxsReqPacket:请求交易的包,encode时包括如下字段: +std::shared_ptr> _requestedTxs +``` + ++ SyncStatus.cpp +``` +//三种状态 +//SyncStatus +struct SyncStatus +{ + SyncState state = SyncState::Idle; + PROTOCOL_ID protocolId; + int64_t currentBlockNumber; + int64_t knownHighestNumber; + h256 knownLatestHash; +}; + +//SyncPeerStatus:节点状态 +NodeID nodeId; +int64_t number; +h256 genesisHash; +h256 latestHash; +DownloadRequestQueue reqQueue; +bool isSealer = false; + +//SyncMasterStatus的重要数据结构 +std::map> m_peersStatus; +//SyncMasterStatus的重要函数 +NodeIDs SyncMasterStatus::filterPeers(int64_t const& _neighborSize, std::shared_ptr _peers, + std::function)> const& _allow) +{ +} +void SyncMasterStatus::foreachPeer( + std::function)> const& _f) const +{ +} +void SyncMasterStatus::foreachPeerRandom( + std::function)> const& _f) const +{ +} +``` + ++ SyncMsgEngine.cpp +``` +//class DownloadBlocksContainer:重要数据结构 +std::vector m_blockRLPsBatch; + +// class SyncMsgEngine +std::shared_ptr m_txsWorker;//发送syncMsg +std::shared_ptr m_txsSender;//发送tx +std::shared_ptr m_txsReceiver;//接收tx +std::shared_ptr m_timeAlignWorker;//时间对齐 +``` + +``` + +//SyncMsgEngine::messageHandler()-->SyncMsgEngine::interpret() +bool SyncMsgEngine::interpret(SyncMsgPacket::Ptr _packet, dev::p2p::P2PMessage::Ptr _msg, dev::h512 const& _peer) +{ + try + { + auto self = std::weak_ptr(shared_from_this()); + switch (_packet->packetType) + { + //接收处理状态包 + case StatusPacket: + onPeerStatus(*_packet); + break; + //m_txsReceiver工作线程处理:TransactionsPacket,放入交易队列 + case TransactionsPacket: + m_txsReceiver->enqueue([self, _packet, _msg]() { + auto msgEngine = self.lock(); + if (msgEngine) + { + msgEngine->onPeerTransactions(_packet, _msg); + } + }); + break; + //获取区块并放入下载区块队列 + case BlocksPacket: + onPeerBlocks(*_packet); + break; + //记录的请求 + case ReqBlocskPacket: + onPeerRequestBlocks(*_packet); + break; + // receive transaction hash, _msg is only used to ensure the life-time for rlps of _packet + //m_txsWorker工作线程处理:TxsStatusPacket,发送含有交易哈希值的交易集给peer + case TxsStatusPacket: + m_txsWorker->enqueue([self, _packet, _peer, _msg]() { + auto msgEngine = self.lock(); + if (msgEngine) + { + msgEngine->onPeerTxsStatus(_packet, _peer, _msg); + } + }); + break; + // receive txs-requests, _msg is only used to ensure the life-time for rlps of _packet + //m_txsSender工作线程处理:TxsRequestPacekt, 发送含有交易集的包给peer + case TxsRequestPacekt: + m_txsSender->enqueue([self, _packet, _peer, _msg]() { + auto msgEngine = self.lock(); + if (msgEngine) + { + msgEngine->onReceiveTxsRequest(_packet, _peer, _msg); + } + }); + break; + default: + return false; + } + } + catch (std::exception& e) + { + return false; + } + return true; +} +``` + +``` +std::shared_ptr m_syncStatus; +/** +m_syncStatus处理这三种类型的消息:StatusPacket、BlocksPacket、ReqBlocskPacket +*/ +//处理状态消息包 +void SyncMsgEngine::onPeerStatus(SyncMsgPacket const& _packet) +{ +m_syncStatus->newSyncPeerStatus(info); +} + +//获取区块并放入下载区块队列 +void SyncMsgEngine::onPeerBlocks(SyncMsgPacket const& _packet) +{ +RLP const& rlps = _packet.rlp(); +m_syncStatus->bq().push(rlps); +} + +//记录的请求 +void SyncMsgEngine::onPeerRequestBlocks(SyncMsgPacket const& _packet) +{ +RLP const& rlp = _packet.rlp(); +// request +int64_t from = rlp[0].toInt(); +unsigned size = rlp[1].toInt(); +auto peerStatus = m_syncStatus->peerStatus(_packet.nodeId); +peerStatus->reqQueue.push(from, (int64_t)size); +} +``` + +``` +auto self = std::weak_ptr(shared_from_this()); +auto msgEngine = self.lock(); +/** +msgEngine处理这三种类型的消息:TransactionsPacket、TxsStatusPacket、TxsRequestPacekt +*/ + +//m_txsReceiver工作线程处理:TransactionsPacket,放入交易队列 +msgEngine->onPeerTransactions(_packet, _msg); +void SyncMsgEngine::onPeerTransactions(SyncMsgPacket::Ptr _packet, dev::p2p::P2PMessage::Ptr _msg) +{ +m_txQueue->push(_packet, _msg, _packet->nodeId); +} + +//m_txsWorker工作线程处理:TxsStatusPacket,发送含有交易哈希值的交易集给peer +msgEngine->onPeerTxsStatus(_packet, _peer, _msg); +void SyncMsgEngine::onPeerTxsStatus( + std::shared_ptr _packet, dev::h512 const& _peer, dev::p2p::P2PMessage::Ptr) +{ +// pop all downloaded txs into the txPool +m_txQueue->pop2TxPool(m_txPool); + +RLP const& rlps = _packet->rlp(); +std::set txsHash = rlps[1].toSet(); +//过滤一些未知的交易哈希 +auto requestTxs = m_txPool->filterUnknownTxs(txsHash, _peer); +std::shared_ptr txsReqPacket = std::make_shared(); +txsReqPacket->encode(requestTxs); +auto p2pMsg = txsReqPacket->toMessage(m_protocolId); +// send request to the peer +m_service->asyncSendMessageByNodeID(_peer, p2pMsg, nullptr); +} + +//m_txsSender工作线程处理:TxsRequestPacekt, 发送含有交易集的包给peer +msgEngine->onReceiveTxsRequest(_packet, _peer, _msg); +void SyncMsgEngine::onReceiveTxsRequest( + std::shared_ptr _txsReqPacket, dev::h512 const& _peer, dev::p2p::P2PMessage::Ptr) +{ +RLP const& rlps = _txsReqPacket->rlp(); +std::vector reqTxs = rlps[0].toVector(); +//从请求包或者的交易哈希集合,从交易池捞出真实的交易 +auto txs = m_txPool->obtainTransactions(reqTxs); +std::shared_ptr> txRLPs = std::make_shared>(); + //遍历交易集,并打包 + for (auto tx : *txs) + { + txRLPs->emplace_back(tx->rlp(WithSignature)); + tx->appendNodeContainsTransaction(_peer); + } + std::shared_ptr txsPacket = + std::make_shared(); + txsPacket->encode(*txRLPs); + auto p2pMsg = txsPacket->toMessage(m_protocolId); + //发送含有交易集的包给peer + m_service->asyncSendMessageByNodeID(_peer, p2pMsg, CallbackFuncWithSession(), Options()); + +} +``` + ++ SyncTransaction.cpp +``` +//重要数据结构 +/// Block queue and peers +std::shared_ptr m_syncStatus; +/// Message handler of p2p +std::shared_ptr m_msgEngine; +std::shared_ptr m_txQueue; +void SyncTransaction::doWork() +{ + maintainDownloadingTransactions(); + maintainTransactions(); + forwardRemainingTxs(); + } + +void SyncTransaction::maintainDownloadingTransactions() +{ + m_txQueue->pop2TxPool(m_txPool); +} + +//SyncTransaction::maintainTransactions()-->sendTransactions +()-->broadcastTransactions() +void SyncTransaction::sendTransactions(std::shared_ptr _ts, + bool const& _fastForwardRemainTxs, int64_t const& _startIndex) +{ +// send the transactions from RPC +broadcastTransactions(selectedPeers, _ts,_fastForwardRemainTxs, _startIndex); + +sendTxsStatus(_ts, selectedPeers); + +} + +void SyncTransaction::broadcastTransactions(std::shared_ptr _selectedPeers, + std::shared_ptr _ts, bool const& _fastForwardRemainTxs, + int64_t const& _startIndex) +{ +peers = m_syncStatus->filterPeers() +m_syncStatus->foreachPeerRandom([&](shared_ptr _p) { +m_service->asyncSendMessageByNodeID(_p->nodeId, msg, CallbackFuncWithSession(), Options()); +}); +``` + ++ SyncTreeTopology.cpp + +``` +// the nodeList include both the consensus nodes and the observer nodes +std::shared_ptr m_nodeList; + +std::shared_ptr SyncTreeTopology::selectNodesForBlockSync( + std::shared_ptr> _peers) +{ + Guard l(m_mutex); + std::shared_ptr selectedNodeList = std::make_shared(); + // the node doesn't locate in the group + if (!locatedInGroup()) + { + return selectedNodeList; + } + // here will not overflow + // the sync-tree-toplogy is: + // consensusNode(0)->{0->{2,3}, 1->{4,5}} + // however, the tree-toplogy is: + // consensusNode(0)->{1->{3,4}, 2->{5,6}} + // so every node of tree-toplogy should decrease 1 to get sync-tree-toplogy + int64_t offset = m_startIndex - 1; + + int64_t nodeIndex = m_nodeIndex + 1 - m_startIndex; + // find the parent nodes + selectParentNodes(selectedNodeList, _peers, nodeIndex, offset); + + // the node is the consensusNode, chose the childNode + if (m_consIndex >= 0) + { + recursiveSelectChildNodes(selectedNodeList, 0, _peers, offset); + } + // the node is not the consensusNode + else + { + recursiveSelectChildNodes(selectedNodeList, nodeIndex, _peers, offset); + } + + return selectedNodeList; +} +``` + ++ SyncMaster.cpp +``` +//重要数据结构 +/// Downloading txs queue +std::shared_ptr m_txQueue; +/// Block queue and peers +std::shared_ptr m_syncStatus; +/// Message handler of p2p +std::shared_ptr m_msgEngine; +dev::ThreadPool::Ptr m_downloadBlockProcessor = nullptr; +dev::ThreadPool::Ptr m_sendBlockProcessor = nullptr; +// sync transactions +SyncTransaction::Ptr m_syncTrans = nullptr; +// handler for find the tree router +SyncTreeTopology::Ptr m_syncTreeRouter = nullptr; +// thread to gossip block status +GossipBlockStatus::Ptr m_blockStatusGossipThread = nullptr; +// settings +dev::eth::Handler m_blockSubmitted; +``` + +``` +//SyncMaster::start()--> startWorking()-->workLoop()-->SyncMaster::doWork() +void SyncMaster::start() +{ + startWorking(); + m_syncTrans->start(); + if (m_blockStatusGossipThread) + { + m_blockStatusGossipThread->start(); + } +} +void SyncMaster::doWork() +{ +// maintain the connections between observers/sealers +maintainPeersConnection(); +m_downloadBlockProcessor->enqueue([this]() { + try + { + // flush downloaded buffer into downloading queue + maintainDownloadingQueueBuffer(); + // Not Idle do + if (isSyncing()) + { + // check and commit the downloaded block + if (m_syncStatus->state == SyncState::Downloading) + { + bool finished = maintainDownloadingQueue(); + if (finished) + noteDownloadingFinish(); + } + } + // send block-download-request to peers if this node is behind others + maintainPeersStatus(); + } +}); +// send block-status to other nodes when commit a new block,即StatusPacket类型 +maintainBlocks(); +// send block to other nodes +m_sendBlockProcessor->enqueue([this]() {maintainBlockRequest();}); +} + +void SyncMaster::maintainPeersStatus() +{ +//会下载区块同步节点状态 +// Start download +noteDownloadingBegin(); +SyncReqBlockPacket packet; +unsigned size = to - from + 1; +packet.encode(from, size); +m_service->asyncSendMessageByNodeID( + _p->nodeId, packet.toMessage(m_protocolId), CallbackFuncWithSession(), Options()); + +} +``` +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ \ No newline at end of file diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libtxpool.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libtxpool.md new file mode 100644 index 000000000..489e5f43c --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/libtxpool.md @@ -0,0 +1,67 @@ +# libtxpool 模块 +作者:TrustChain [微信公众号] + +libtxpool:交易池。 + +## 主要内容有: + ++ 交易流:客户端(创建交易|交易签名)-->接入节点(交易验签|交易池处理|交易广播);备注:客户端通过Channel或RPC信道将交易发送给接入节点。 ++ 缓存区块交易nonce:缓存最近commited区块及交易nonce。 + +![](../../../../images/articles/sourceCode_knowledge_map/libtxpool.png) + +## 涉及知识点: + ++ 交易池:记录客户端提交的交易。 + +1. 交易分类:一类是部署合约的交易,一类是调用合约的交易; + +2. 交易验签:私钥签名,公钥验签。 + +3. 交易验重:m_txNonceCheck+m_txpoolNonceChecker; + +4. 数据结构: +``` +// transaction queue +using TransactionQueue = std::set; +TransactionQueue m_txsQueue; +std::unordered_map m_txsHash; +// dropped transaction queue +h256Hash m_dropped; +//记录交易池的交易nonce +std::shared_ptr m_txpoolNonceChecker; +``` + ++ 交易池处理:把交易提交到submitPool的队列,检查其他节点同步状态(sealerList节点+observerList节点),交易import到队列成功,并返回通知notifyReceipt给客户端;把交易的rpccallback回调函数放入workerPool的队列做进一步处理。 + +``` +TxPool::submit:m_submitPool->enqueue--》m_syncStatusChecker--》TxPool::import--》TxPool::notifyReceipt; +TxPool::import:setImportTime、check m_maxMemoryLimit、check txpool size、verify (nonce&signature); +TxPool::notifyReceipt:m_workerPool->enqueue([callback, receipt]; +TxPool::verify:check TX in m_txsHash(交易池)--》check TX in m_dropped(丢弃交易集合)--》check nonce【m_txNonceCheck】--》check transaction signature【Transaction::sender函数】--》txPoolNonceCheck--》check chainId and groupId。 +``` + ++ 缓存区块交易nonce:记录已commited区块及交易nonce。 +``` + + +//记录交易池缓存的最近区块的交易nonce +std::shared_ptr m_txNonceCheck; +//记录无效的交易 +std::shared_ptr> m_invalidTxs; +std::shared_ptr> m_txsHashFilter; + // dropped transaction queue +h256Hash m_dropped; +``` + ++ 丢弃缓存区块:commited区块、bad 区块。 +``` +TxPool::dropTransactions--》 +TxPool::dropBlockTxsFilter【在 m_txsHashFilter去除对应区块的交易】--》 +TxPool::removeInvalidTxs【removeTrans()、m_dropped.insert()、m_txpoolNonceChecker->delCache()、m_txsHashFilter->erase()】。 +``` +参考文献: + +[1] https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v2.7.2 + +[2] https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/ \ No newline at end of file diff --git a/2.x/docs/articles/7_practice/sourceCode_knowledge_map/sourceCode_knowledge_map.md b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/sourceCode_knowledge_map.md new file mode 100644 index 000000000..9219195c2 --- /dev/null +++ b/2.x/docs/articles/7_practice/sourceCode_knowledge_map/sourceCode_knowledge_map.md @@ -0,0 +1,115 @@ +# FISCO BCOS 源码知识图谱 +作者:TrustChain [微信公众号] + + + +## 一、源码知识图谱: ++ 客户端(创建交易|交易签名)-->接入节点(交易验签|交易广播|交易池处理)-->共识节点(交易排序打包|执行区块|区块共识)--广播区块-->全网节点(验证交易|存储交易)。 + +![](../../../../images/articles/sourceCode_knowledge_map/sourceCode_knowledge_map.png) + +## 二、开源生态 + +![](../../../../images/articles/sourceCode_knowledge_map/Fisco_Bcos_OSE.png) + +## 三、依赖库 ++ TBB:https://github.com/oneapi-src/oneTBB + ++ Boost:https://github.com/boostorg/boost + ++ Snappy:https://github.com/google/snappy + ++ LevelDB:https://github.com/google/leveldb + ++ RocksDB:https://github.com/facebook/rocksdb + ++ Cryptopp:https://github.com/weidai11/cryptopp + ++ Secp256k1:https://github.com/FISCO-BCOS/secp256k1 + ++ JsonCpp:https://github.com/open-source-parsers/jsoncpp + ++ JsonRpcCpp:https://github.com/cinemast/libjson-rpc-cpp + ++ TASSL:https://github.com/jntass/TASSL + ++ TCMalloc:https://github.com/google/tcmalloc + ++ LibFF:https://github.com/scipr-lab/libff + ++ GroupSig:https://github.com/FISCO-BCOS/group-signature-lib + ++ EVMC:https://github.com/FISCO-BCOS/evmc/ + ++ EVMONE:https://github.com/FISCO-BCOS/evmone/ + ++ MHD:https://raw.githubusercontent.com/FISCO-BCOS/LargeFiles/master/libs/libmicrohttpd-0.9.63.tar.gz + ++ Libzdb:https://cdn.mysql.com/archives/mysql-connector-c/mysql-connector-c-6.1.11-src.tar.gz + ++ Paillier:https://github.com/FISCO-BCOS/paillier-lib/ + ++ VRF:https://github.com/cyjseagull/vrf + ++ MPIR:https://github.com/FISCO-BCOS/mpir/ + +## 四、学习笔记 + ++ [1. Fisco-Bcos>libethcore 模块](libethcore.md ':include') + ++ [2. Fisco-Bcos>libtxpool 模块](libtxpool.md ':include') + ++ [3. Fisco-Bcos>libdevcore 模块](libdevcore.md ':include') + ++ [4. Fisco-Bcos>libdevcrypto 模块](libdevcrypto.md ':include') + ++ [5. Fisco-Bcos>libstorage 模块](libstorage.md ':include') + ++ [6. Fisco-Bcos>libstoragestate 模块](libstoragestate.md ':include') + ++ [7. Fisco-Bcos>libmptstate 模块](libmptstate.md ':include') + ++ [8. Fisco-Bcos>libblockchain 模块](libblockchain.md ':include') + ++ [9. Fisco-Bcos>libeventfilter 模块](libeventfilter.md ':include') + ++ [10. Fisco-Bcos>libledger 模块](libledger.md ':include') + ++ [11. Fisco-Bcos>libinitializer 模块](libinitializer.md ':include') + ++ [12. Fisco-Bcos>libconfig 模块](libconfig.md ':include') + ++ [13. Fisco-Bcos>libsecurity 模块](libsecurity.md ':include') + ++ [14. Fisco-Bcos>libnetwork 模块](libnetwork.md ':include') + ++ [15. Fisco-Bcos>libp2p 模块](libp2p.md ':include') + ++ [16. Fisco-Bcos>librpc 模块](librpc.md ':include') + ++ [17. Fisco-Bcos>libchannelserver 模块](libchannelserver.md ':include') + ++ [18. Fisco-Bcos>libstat 模块](libstat.md ':include') + ++ [19. Fisco-Bcos>libflowlimit 模块](libflowlimit.md ':include') + ++ [20. Fisco-Bcos>libsync 模块](libsync.md ':include') + ++ [21. Fisco-Bcos>libconsensus 模块](libconsensus.md ':include') + + - [libconsensus_pbft 子模块](libconsensus_pbft.md ':include') + - [libconsensus_raft 子模块](libconsensus_raft.md ':include') + - [libconsensus_rotating_pbft 子模块](libconsensus_rotating_pbft.md ':include') + ++ [22. Fisco-Bcos>libblockverifier 模块](libblockverifier.md ':include') + ++ [23. Fisco-Bcos>libexecutive 模块](libexecutive.md ':include') + ++ [24. Fisco-Bcos>libprecompiled 模块](libprecompiled.md ':include') + ++ [25. Fisco-Bcos>libinterpreter 模块](libinterpreter.md ':include') + + + + + diff --git a/2.x/images/articles/sourceCode_knowledge_map/Fisco_Bcos_OSE.png b/2.x/images/articles/sourceCode_knowledge_map/Fisco_Bcos_OSE.png new file mode 100644 index 000000000..cf06121fe Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/Fisco_Bcos_OSE.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libblockchain.png b/2.x/images/articles/sourceCode_knowledge_map/libblockchain.png new file mode 100644 index 000000000..592efef1c Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libblockchain.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libblockverifier.png b/2.x/images/articles/sourceCode_knowledge_map/libblockverifier.png new file mode 100644 index 000000000..6f1260dec Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libblockverifier.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libchannelserver.png b/2.x/images/articles/sourceCode_knowledge_map/libchannelserver.png new file mode 100644 index 000000000..ac4a6352a Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libchannelserver.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libconfig.png b/2.x/images/articles/sourceCode_knowledge_map/libconfig.png new file mode 100644 index 000000000..a6eebf11a Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libconfig.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libconsensus.png b/2.x/images/articles/sourceCode_knowledge_map/libconsensus.png new file mode 100644 index 000000000..ef22bdfa3 Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libconsensus.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libconsensus_pbft.png b/2.x/images/articles/sourceCode_knowledge_map/libconsensus_pbft.png new file mode 100644 index 000000000..91b9a2838 Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libconsensus_pbft.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libconsensus_raft.png b/2.x/images/articles/sourceCode_knowledge_map/libconsensus_raft.png new file mode 100644 index 000000000..940fbaab1 Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libconsensus_raft.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libconsensus_rotating_pbft.png b/2.x/images/articles/sourceCode_knowledge_map/libconsensus_rotating_pbft.png new file mode 100644 index 000000000..f53fbcc04 Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libconsensus_rotating_pbft.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libdevcore.png b/2.x/images/articles/sourceCode_knowledge_map/libdevcore.png new file mode 100644 index 000000000..73aed8c92 Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libdevcore.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libdevcrypto.png b/2.x/images/articles/sourceCode_knowledge_map/libdevcrypto.png new file mode 100644 index 000000000..713f13c90 Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libdevcrypto.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libdevcrypto_1.PNG b/2.x/images/articles/sourceCode_knowledge_map/libdevcrypto_1.PNG new file mode 100644 index 000000000..841da331f Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libdevcrypto_1.PNG differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libethcore.png b/2.x/images/articles/sourceCode_knowledge_map/libethcore.png new file mode 100644 index 000000000..b0cc45ef1 Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libethcore.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libeventfilter.png b/2.x/images/articles/sourceCode_knowledge_map/libeventfilter.png new file mode 100644 index 000000000..5a81c8c73 Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libeventfilter.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libexecutive.png b/2.x/images/articles/sourceCode_knowledge_map/libexecutive.png new file mode 100644 index 000000000..f147aab7b Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libexecutive.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libflowlimit.png b/2.x/images/articles/sourceCode_knowledge_map/libflowlimit.png new file mode 100644 index 000000000..7883ea7fc Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libflowlimit.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libinitializer.png b/2.x/images/articles/sourceCode_knowledge_map/libinitializer.png new file mode 100644 index 000000000..7a97b5a28 Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libinitializer.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libinterpreter.png b/2.x/images/articles/sourceCode_knowledge_map/libinterpreter.png new file mode 100644 index 000000000..8963a949a Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libinterpreter.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libledger.png b/2.x/images/articles/sourceCode_knowledge_map/libledger.png new file mode 100644 index 000000000..0fd9489c8 Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libledger.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libmptstate.png b/2.x/images/articles/sourceCode_knowledge_map/libmptstate.png new file mode 100644 index 000000000..21d574d61 Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libmptstate.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libnetwork.png b/2.x/images/articles/sourceCode_knowledge_map/libnetwork.png new file mode 100644 index 000000000..e9f569a8b Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libnetwork.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libp2p.png b/2.x/images/articles/sourceCode_knowledge_map/libp2p.png new file mode 100644 index 000000000..beb7d7790 Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libp2p.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libprecompiled-extension.png b/2.x/images/articles/sourceCode_knowledge_map/libprecompiled-extension.png new file mode 100644 index 000000000..c1c3bd36f Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libprecompiled-extension.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libprecompiled.png b/2.x/images/articles/sourceCode_knowledge_map/libprecompiled.png new file mode 100644 index 000000000..178ed3cb7 Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libprecompiled.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/librpc.png b/2.x/images/articles/sourceCode_knowledge_map/librpc.png new file mode 100644 index 000000000..e28425ede Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/librpc.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libsecurity.png b/2.x/images/articles/sourceCode_knowledge_map/libsecurity.png new file mode 100644 index 000000000..54c5fe072 Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libsecurity.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libstat.png b/2.x/images/articles/sourceCode_knowledge_map/libstat.png new file mode 100644 index 000000000..a2bf90ef7 Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libstat.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libstorage.png b/2.x/images/articles/sourceCode_knowledge_map/libstorage.png new file mode 100644 index 000000000..e5ec44233 Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libstorage.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libstoragestate.png b/2.x/images/articles/sourceCode_knowledge_map/libstoragestate.png new file mode 100644 index 000000000..5e31af1ad Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libstoragestate.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libsync.png b/2.x/images/articles/sourceCode_knowledge_map/libsync.png new file mode 100644 index 000000000..88a69bb9e Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libsync.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/libtxpool.png b/2.x/images/articles/sourceCode_knowledge_map/libtxpool.png new file mode 100644 index 000000000..89bafb9b4 Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/libtxpool.png differ diff --git a/2.x/images/articles/sourceCode_knowledge_map/sourceCode_knowledge_map.png b/2.x/images/articles/sourceCode_knowledge_map/sourceCode_knowledge_map.png new file mode 100644 index 000000000..d4b500e3d Binary files /dev/null and b/2.x/images/articles/sourceCode_knowledge_map/sourceCode_knowledge_map.png differ