From 4acf319e3072a09a1bd3415e1d7551512dd9bf33 Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Mon, 25 Mar 2024 17:28:03 +0800 Subject: [PATCH 01/39] (rpc): add eth json rpc interfaces. (#4318) --- bcos-rpc/bcos-rpc/Common.h | 57 +++++++++- bcos-rpc/bcos-rpc/RpcFactory.cpp | 4 + .../jsonrpc/DupTestTxJsonRpcImpl_2_0.h | 100 ------------------ bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.h | 2 +- .../bcos-rpc/jsonrpc/JsonRpcInterface.cpp | 2 +- bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h | 3 +- bcos-rpc/bcos-rpc/jsonrpc/Web3JsonRpcImpl.cpp | 24 +++++ bcos-rpc/bcos-rpc/jsonrpc/Web3JsonRpcImpl.h | 68 ++++++++++++ .../jsonrpc/endpoints/EndpointInterface.h | 45 ++++++++ .../jsonrpc/endpoints/EthEndpoint.cpp | 28 +++++ .../bcos-rpc/jsonrpc/endpoints/EthEndpoint.h | 85 +++++++++++++++ .../jsonrpc/endpoints/NetEndpoint.cpp | 28 +++++ .../bcos-rpc/jsonrpc/endpoints/NetEndpoint.h | 46 ++++++++ .../jsonrpc/endpoints/Web3Endpoint.cpp | 27 +++++ .../bcos-rpc/jsonrpc/endpoints/Web3Endpoint.h | 44 ++++++++ bcos-table/src/LegacyStorageWrapper.h | 4 +- .../bcos-tars-protocol/tars/Transaction.tars | 2 + 17 files changed, 459 insertions(+), 110 deletions(-) delete mode 100644 bcos-rpc/bcos-rpc/jsonrpc/DupTestTxJsonRpcImpl_2_0.h create mode 100644 bcos-rpc/bcos-rpc/jsonrpc/Web3JsonRpcImpl.cpp create mode 100644 bcos-rpc/bcos-rpc/jsonrpc/Web3JsonRpcImpl.h create mode 100644 bcos-rpc/bcos-rpc/jsonrpc/endpoints/EndpointInterface.h create mode 100644 bcos-rpc/bcos-rpc/jsonrpc/endpoints/EthEndpoint.cpp create mode 100644 bcos-rpc/bcos-rpc/jsonrpc/endpoints/EthEndpoint.h create mode 100644 bcos-rpc/bcos-rpc/jsonrpc/endpoints/NetEndpoint.cpp create mode 100644 bcos-rpc/bcos-rpc/jsonrpc/endpoints/NetEndpoint.h create mode 100644 bcos-rpc/bcos-rpc/jsonrpc/endpoints/Web3Endpoint.cpp create mode 100644 bcos-rpc/bcos-rpc/jsonrpc/endpoints/Web3Endpoint.h diff --git a/bcos-rpc/bcos-rpc/Common.h b/bcos-rpc/bcos-rpc/Common.h index 28d1daff89..83e861aeb1 100644 --- a/bcos-rpc/bcos-rpc/Common.h +++ b/bcos-rpc/bcos-rpc/Common.h @@ -24,9 +24,7 @@ #define RPC_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("RPC") -namespace bcos -{ -namespace rpc +namespace bcos::rpc { enum AMOPClientMessageType { @@ -35,5 +33,54 @@ enum AMOPClientMessageType AMOP_BROADCAST = 0x112, // 274 AMOP_RESPONSE = 0x113 // 275 }; -} // namespace rpc -} // namespace bcos \ No newline at end of file + +namespace eth +{ +constexpr std::string_view web3_clientVersion{"web3_clientVersion"}; +constexpr std::string_view web3_sha3{"web3_sha3"}; +constexpr std::string_view net_version{"net_version"}; +constexpr std::string_view net_listening{"net_listening"}; +constexpr std::string_view net_peerCount{"net_peerCount"}; +constexpr std::string_view eth_protocolVersion{"eth_protocolVersion"}; +constexpr std::string_view eth_syncing{"eth_syncing"}; +constexpr std::string_view eth_coinbase{"eth_coinbase"}; +constexpr std::string_view eth_chainId{"eth_chainId"}; +constexpr std::string_view eth_mining{"eth_mining"}; +constexpr std::string_view eth_hashrate{"eth_hashrate"}; +constexpr std::string_view eth_gasPrice{"eth_gasPrice"}; +constexpr std::string_view eth_accounts{"eth_accounts"}; +constexpr std::string_view eth_blockNumber{"eth_blockNumber"}; +constexpr std::string_view eth_getBalance{"eth_getBalance"}; +constexpr std::string_view eth_getStorageAt{"eth_getStorageAt"}; +constexpr std::string_view eth_getTransactionCount{"eth_getTransactionCount"}; +constexpr std::string_view eth_getBlockTxCountByHash{"eth_getBlockTransactionCountByHash"}; +constexpr std::string_view eth_getBlockTxCountByNumber{"eth_getBlockTransactionCountByNumber"}; +constexpr std::string_view eth_getUncleCountByBlockHash{"eth_getUncleCountByBlockHash"}; +constexpr std::string_view eth_getUncleCountByBlockNumber{"eth_getUncleCountByBlockNumber"}; +constexpr std::string_view eth_getCode{"eth_getCode"}; +constexpr std::string_view eth_sign{"eth_sign"}; +constexpr std::string_view eth_sendTransaction{"eth_sendTransaction"}; +constexpr std::string_view eth_signTransaction{"eth_signTransaction"}; +constexpr std::string_view eth_sendRawTransaction{"eth_sendRawTransaction"}; +constexpr std::string_view eth_call{"eth_call"}; +constexpr std::string_view eth_estimateGas{"eth_estimateGas"}; +constexpr std::string_view eth_getBlockByHash{"eth_getBlockByHash"}; +constexpr std::string_view eth_getBlockByNumber{"eth_getBlockByNumber"}; +constexpr std::string_view eth_getTransactionByHash{"eth_getTransactionByHash"}; +constexpr std::string_view eth_getTxByBlockHashAndIndex{"eth_getTransactionByBlockHashAndIndex"}; +constexpr std::string_view eth_getTxByBlockNumAndIndex{"eth_getTransactionByBlockNumberAndIndex"}; +constexpr std::string_view eth_getTransactionReceipt{"eth_getTransactionReceipt"}; +constexpr std::string_view eth_getUncleByBlockHashAndIndex{"eth_getUncleByBlockHashAndIndex"}; +constexpr std::string_view eth_getUncleByBlockNumberAndIndex{"eth_getUncleByBlockNumberAndIndex"}; +constexpr std::string_view eth_newFilter{"eth_newFilter"}; +constexpr std::string_view eth_newBlockFilter{"eth_newBlockFilter"}; +constexpr std::string_view eth_newPendingTransactionFilter{"eth_newPendingTransactionFilter"}; +constexpr std::string_view eth_uninstallFilter{"eth_uninstallFilter"}; +constexpr std::string_view eth_getFilterChanges{"eth_getFilterChanges"}; +constexpr std::string_view eth_getFilterLogs{"eth_getFilterLogs"}; +constexpr std::string_view eth_getLogs{"eth_getLogs"}; + +// constexpr std::string_view eth_createAccessList{"eth_createAccessList"}; +// constexpr std::string_view eth_maxPriorityFeePerGas{"eth_maxPriorityFeePerGas"}; +} // namespace eth +} // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/RpcFactory.cpp b/bcos-rpc/bcos-rpc/RpcFactory.cpp index b1f17fdedf..1f1a3ab634 100644 --- a/bcos-rpc/bcos-rpc/RpcFactory.cpp +++ b/bcos-rpc/bcos-rpc/RpcFactory.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -335,6 +336,9 @@ bcos::boostssl::ws::WsService::Ptr RpcFactory::buildWsService( bcos::rpc::JsonRpcImpl_2_0::Ptr RpcFactory::buildJsonRpc(int sendTxTimeout, const std::shared_ptr& _wsService, GroupManager::Ptr _groupManager) { + // auto web3Rpc = std::make_shared(_groupManager, m_gateway, + // _wsService); + // JsonRpcImpl_2_0 //* auto jsonRpcInterface = diff --git a/bcos-rpc/bcos-rpc/jsonrpc/DupTestTxJsonRpcImpl_2_0.h b/bcos-rpc/bcos-rpc/jsonrpc/DupTestTxJsonRpcImpl_2_0.h deleted file mode 100644 index 03d5935007..0000000000 --- a/bcos-rpc/bcos-rpc/jsonrpc/DupTestTxJsonRpcImpl_2_0.h +++ /dev/null @@ -1,100 +0,0 @@ -// -// Created by Jimmy Shi on 2022/6/11. -// -#pragma once -#include "DuplicateTransactionFactory.h" -#include "JsonRpcImpl_2_0.h" -#include -#include -#include - -namespace bcos::rpc -{ -class DupTestTxJsonRpcImpl_2_0 : public JsonRpcImpl_2_0 -{ -public: - using Ptr = std::shared_ptr; - DupTestTxJsonRpcImpl_2_0(GroupManager::Ptr _groupManager, - bcos::gateway::GatewayInterface::Ptr _gatewayInterface, - std::shared_ptr _wsService) - : JsonRpcImpl_2_0(_groupManager, _gatewayInterface, _wsService) - {} - - // duplicate many tx to txpool - void sendTransaction(std::string_view _groupID, std::string_view _nodeName, - std::string_view _data, bool _requireProof, RespFunc _respFunc) override - { - // send directly - JsonRpcImpl_2_0::sendTransaction( - _groupID, _nodeName, _data, _requireProof, std::move(_respFunc)); - // duplicate many tx into txpool - - auto transactionData = decodeData(_data); - auto nodeService = getNodeService(_groupID, _nodeName, "sendTransaction"); - auto txpool = nodeService->txpool(); - checkService(txpool, "txpool"); - - // Note: avoid call tx->sender() or tx->verify() here in case of verify the same transaction - // more than once - auto tx = nodeService->blockFactory()->transactionFactory()->createTransaction( - bcos::ref(transactionData), false); - - - if (tx->to().empty()) - { - // ignore deploy tx - return; - } - - RPC_IMPL_LOG(TRACE) << LOG_DESC("duplicate sendTransaction") << LOG_KV("group", _groupID) - << LOG_KV("node", _nodeName); - - auto submitCallback = - [_requireProof](Error::Ptr _error, - bcos::protocol::TransactionSubmitResult::Ptr _transactionSubmitResult) { - if (_error && _error->errorCode() != bcos::protocol::CommonError::SUCCESS) - { - RPC_IMPL_LOG(ERROR) - << LOG_BADGE("sendTransaction") << LOG_KV("requireProof", _requireProof) - << LOG_KV("hash", _transactionSubmitResult ? - _transactionSubmitResult->txHash().abridged() : - "unknown") - << LOG_KV("code", _error->errorCode()) - << LOG_KV("message", _error->errorMessage()); - - - return; - } - }; - - // generate account - bcos::crypto::KeyPairInterface::Ptr keyPair = - nodeService->blockFactory()->cryptoSuite()->signatureImpl()->generateKeyPair(); - - // send many tx to txpool - int64_t dup = 49; // 1 tx can generate 49 + 1 tx(include original tx) - DuplicateTransactionFactory::asyncMultiBuild( - nodeService->blockFactory()->transactionFactory(), transactionData, keyPair, dup, - [submitCallback = std::move(submitCallback), txpool]( - bcos::protocol::Transaction::Ptr tx) { - // std::cout << "sendtx: " << tx->nonce() << std::endl; - task::wait([](decltype(txpool) txpool, decltype(tx) tx, - decltype(submitCallback) submitCallback) -> task::Task { - try - { - auto submitResult = co_await txpool->submitTransaction(std::move(tx)); - submitCallback(nullptr, submitResult); - } - catch (bcos::Error& e) - { - submitCallback(std::make_shared(std::move(e)), nullptr); - } - }(txpool, std::move(tx), std::move(submitCallback))); - }); - } - - ~DupTestTxJsonRpcImpl_2_0() {} -}; - - -} // namespace bcos::rpc diff --git a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.h b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.h index 355f0bdfd0..57e80cf145 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.h +++ b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.h @@ -153,7 +153,7 @@ class JsonRpcImpl_2_0 : public JsonRpcInterface, } } -private: +protected: void gatewayInfoToJson(Json::Value& _response, bcos::gateway::GatewayInfo::Ptr _gatewayInfo); void gatewayInfoToJson(Json::Value& _response, bcos::gateway::GatewayInfo::Ptr _localP2pInfo, bcos::gateway::GatewayInfosPtr _peersInfo); diff --git a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.cpp b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.cpp index 9d79d818e2..e3b3bbfc8b 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.cpp +++ b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.cpp @@ -133,7 +133,7 @@ void JsonRpcInterface::onRPCRequest(std::string_view _requestBody, Sender _sende << LOG_KV("request", _requestBody) << LOG_KV("response", std::string_view((const char*)strResp.data(), strResp.size())); - _sender(strResp); + _sender(std::move(strResp)); } void bcos::rpc::parseRpcRequestJson(std::string_view _requestBody, JsonRequest& _jsonRequest) diff --git a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h index ca61d95140..a25ee0a5ec 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h +++ b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h @@ -33,6 +33,7 @@ namespace bcos::rpc { using Sender = std::function; using RespFunc = std::function; +using MethodMap = std::unordered_map>; class JsonRpcInterface { @@ -122,7 +123,7 @@ class JsonRpcInterface public: void onRPCRequest(std::string_view _requestBody, Sender _sender); -private: +protected: void initMethod(); std::unordered_map> m_methodToFunc; diff --git a/bcos-rpc/bcos-rpc/jsonrpc/Web3JsonRpcImpl.cpp b/bcos-rpc/bcos-rpc/jsonrpc/Web3JsonRpcImpl.cpp new file mode 100644 index 0000000000..dae84ee404 --- /dev/null +++ b/bcos-rpc/bcos-rpc/jsonrpc/Web3JsonRpcImpl.cpp @@ -0,0 +1,24 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file Web3JsonRpcImpl.cpp + * @author: kyonGuo + * @date 2024/3/21 + */ + +#include "Web3JsonRpcImpl.h" + +using namespace bcos; +using namespace bcos::rpc; diff --git a/bcos-rpc/bcos-rpc/jsonrpc/Web3JsonRpcImpl.h b/bcos-rpc/bcos-rpc/jsonrpc/Web3JsonRpcImpl.h new file mode 100644 index 0000000000..49cceb33cd --- /dev/null +++ b/bcos-rpc/bcos-rpc/jsonrpc/Web3JsonRpcImpl.h @@ -0,0 +1,68 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file Web3JsonRpcImpl.h + * @author: kyonGuo + * @date 2024/3/21 + */ + +#pragma once +#include "bcos-rpc/groupmgr/GroupManager.h" +#include "bcos-rpc/jsonrpc/JsonRpcImpl_2_0.h" +#include "bcos-rpc/jsonrpc/endpoints/EndpointInterface.h" +#include "bcos-rpc/jsonrpc/endpoints/EthEndpoint.h" +#include "bcos-rpc/jsonrpc/endpoints/NetEndpoint.h" +#include "bcos-rpc/jsonrpc/endpoints/Web3Endpoint.h" +#include "bcos-rpc/validator/CallValidator.h" +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::rpc +{ +class EthEndpoint; +class NetEndpoint; +class Web3Endpoint; +class Web3JsonRpcImpl : public JsonRpcImpl_2_0, public std::enable_shared_from_this +{ +public: + Web3JsonRpcImpl(GroupManager::Ptr _groupManager, + bcos::gateway::GatewayInterface::Ptr _gatewayInterface, + std::shared_ptr _wsService) + : JsonRpcImpl_2_0( + std::move(_groupManager), std::move(_gatewayInterface), std::move(_wsService)), + m_ethEntryPoint(std::make_unique(m_groupManager)), + m_netEntryPoint(std::make_unique(m_groupManager)), + m_web3EntryPoint(std::make_unique(m_groupManager)) + { + m_methodToFunc.insert( + m_ethEntryPoint->exportMethods().begin(), m_ethEntryPoint->exportMethods().end()); + m_methodToFunc.insert( + m_netEntryPoint->exportMethods().begin(), m_netEntryPoint->exportMethods().end()); + m_methodToFunc.insert( + m_web3EntryPoint->exportMethods().begin(), m_web3EntryPoint->exportMethods().end()); + } + ~Web3JsonRpcImpl() override = default; + +private: + EthEndpoint::UniquePtr m_ethEntryPoint; + NetEndpoint::UniquePtr m_netEntryPoint; + Web3Endpoint::UniquePtr m_web3EntryPoint; +}; +} // namespace bcos::rpc diff --git a/bcos-rpc/bcos-rpc/jsonrpc/endpoints/EndpointInterface.h b/bcos-rpc/bcos-rpc/jsonrpc/endpoints/EndpointInterface.h new file mode 100644 index 0000000000..9813108c68 --- /dev/null +++ b/bcos-rpc/bcos-rpc/jsonrpc/endpoints/EndpointInterface.h @@ -0,0 +1,45 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file EndpointInterface.h + * @author: kyonGuo + * @date 2024/3/21 + */ + +#pragma once + +#include "bcos-rpc/groupmgr/GroupManager.h" +#include + +namespace bcos::rpc +{ +class EndpointInterface +{ +public: + using UniquePtr = std::unique_ptr; + EndpointInterface() = default; + virtual ~EndpointInterface() = default; + EndpointInterface(const EndpointInterface&) = delete; + EndpointInterface& operator=(const EndpointInterface&) = delete; + EndpointInterface(EndpointInterface&&) = delete; + EndpointInterface& operator=(EndpointInterface&&) = delete; + + virtual std::string getEntryName() const = 0; + virtual MethodMap const& exportMethods() = 0; + +protected: + MethodMap m_methods; +}; +} // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/jsonrpc/endpoints/EthEndpoint.cpp b/bcos-rpc/bcos-rpc/jsonrpc/endpoints/EthEndpoint.cpp new file mode 100644 index 0000000000..0af60476a1 --- /dev/null +++ b/bcos-rpc/bcos-rpc/jsonrpc/endpoints/EthEndpoint.cpp @@ -0,0 +1,28 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file EthEndpoint.cpp + * @author: kyonGuo + * @date 2024/3/21 + */ + +#include "EthEndpoint.h" + +using namespace bcos; +using namespace bcos::rpc; + +EthEndpoint::EthEndpoint(bcos::rpc::GroupManager::Ptr groupManager) + : m_groupManager(std::move(groupManager)) +{} diff --git a/bcos-rpc/bcos-rpc/jsonrpc/endpoints/EthEndpoint.h b/bcos-rpc/bcos-rpc/jsonrpc/endpoints/EthEndpoint.h new file mode 100644 index 0000000000..af7ab4727e --- /dev/null +++ b/bcos-rpc/bcos-rpc/jsonrpc/endpoints/EthEndpoint.h @@ -0,0 +1,85 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file EthEndpoint.h + * @author: kyonGuo + * @date 2024/3/21 + */ + +#pragma once +#include "bcos-rpc/groupmgr/GroupManager.h" +#include "bcos-rpc/jsonrpc/endpoints/EndpointInterface.h" +#include +#include +#include +#include +#include + +namespace bcos::rpc +{ + +/** + * eth entry point to match 'eth_' methods + */ +class EthEndpoint : public EndpointInterface +{ +public: + explicit EthEndpoint(bcos::rpc::GroupManager::Ptr group_manager); + std::string getEntryName() const override { return "eth"; } + MethodMap const& exportMethods() override { return m_methods; } + void protoclVersion(RespFunc) {} + void syning(RespFunc) {} + void coinbase(RespFunc) {} + void chainId(RespFunc) {} + void mining(RespFunc) {} + void hashrate(RespFunc) {} + void gasPrice(RespFunc) {} + void accounts(RespFunc) {} + void blockNumber(RespFunc) {} + void getBalance(std::string_view, std::string_view, RespFunc) {} + void getStorageAt(std::string_view, std::string_view, std::string_view, RespFunc) {} + void getTransactionCount(std::string_view, std::string_view, RespFunc) {} + void getBlockTxCountByHash(std::string_view, RespFunc) {} + void getBlockTxCountByNumber(std::string_view, RespFunc) {} + void getUncleCountByBlockHash(std::string_view, RespFunc) {} + void getUncleCountByBlockNumber(std::string_view, RespFunc) {} + void getCode(std::string_view, std::string_view, RespFunc) {} + void sign(std::string_view, std::string_view, RespFunc) {} + void signTransaction(Json::Value const&, RespFunc) {} + void sendTransaction(Json::Value const&, RespFunc) {} + void sendRawTransaction(std::string_view, RespFunc) {} + void call(Json::Value const&, std::string_view, RespFunc) {} + void estimateGas(Json::Value const&, std::string_view, RespFunc) {} + void getBlockByHash(std::string_view, bool, RespFunc) {} + void getBlockByNumber(std::string_view, bool, RespFunc) {} + void getTransactionByHash(std::string_view, RespFunc) {} + void getTransactionByBlockHashAndIndex(std::string_view, std::string_view, RespFunc) {} + void getTransactionByBlockNumberAndIndex(std::string_view, std::string_view, RespFunc) {} + void getTransactionReceipt(std::string_view, RespFunc) {} + void getUncleByBlockHashAndIndex(std::string_view, std::string_view, RespFunc) {} + void getUncleByBlockNumberAndIndex(std::string_view, std::string_view, RespFunc) {} + void newFilter(Json::Value const&, RespFunc) {} + void newBlockFilter(RespFunc) {} + void newPendingTransactionFilter(RespFunc) {} + void uninstallFilter(std::string_view, RespFunc) {} + void getFilterChanges(std::string_view, RespFunc) {} + void getFilterLogs(std::string_view, RespFunc) {} + void getLogs(Json::Value const&, RespFunc) {} + +private: + bcos::rpc::GroupManager::Ptr m_groupManager; +}; + +} // namespace bcos::rpc diff --git a/bcos-rpc/bcos-rpc/jsonrpc/endpoints/NetEndpoint.cpp b/bcos-rpc/bcos-rpc/jsonrpc/endpoints/NetEndpoint.cpp new file mode 100644 index 0000000000..8c83500126 --- /dev/null +++ b/bcos-rpc/bcos-rpc/jsonrpc/endpoints/NetEndpoint.cpp @@ -0,0 +1,28 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file NetEndpoint.cpp + * @author: kyonGuo + * @date 2024/3/21 + */ + +#include "NetEndpoint.h" + +using namespace bcos; +using namespace bcos::rpc; + +NetEndpoint::NetEndpoint(bcos::rpc::GroupManager::Ptr groupManager) + : m_groupManager(std::move(groupManager)) +{} diff --git a/bcos-rpc/bcos-rpc/jsonrpc/endpoints/NetEndpoint.h b/bcos-rpc/bcos-rpc/jsonrpc/endpoints/NetEndpoint.h new file mode 100644 index 0000000000..c54720d5b3 --- /dev/null +++ b/bcos-rpc/bcos-rpc/jsonrpc/endpoints/NetEndpoint.h @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2022 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file NetEndpoint.h + * @author: kyonGuo + * @date 2024/3/21 + */ + +#pragma once +#include "EndpointInterface.h" +#include "bcos-rpc/groupmgr/GroupManager.h" +#include +#include +#include +#include +#include + +namespace bcos::rpc +{ +class NetEndpoint : public EndpointInterface +{ +public: + explicit NetEndpoint(bcos::rpc::GroupManager::Ptr groupManager); + std::string getEntryName() const override { return "net"; } + MethodMap const& exportMethods() override { return m_methods; } + void verison(RespFunc) {} + void listening(RespFunc) {} + void peerCount(RespFunc) {} + +private: + bcos::rpc::GroupManager::Ptr m_groupManager; +}; + +} // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/jsonrpc/endpoints/Web3Endpoint.cpp b/bcos-rpc/bcos-rpc/jsonrpc/endpoints/Web3Endpoint.cpp new file mode 100644 index 0000000000..ef8bb2f8b2 --- /dev/null +++ b/bcos-rpc/bcos-rpc/jsonrpc/endpoints/Web3Endpoint.cpp @@ -0,0 +1,27 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file Web3Endpoint.cpp + * @author: kyonGuo + * @date 2024/3/21 + */ + +#include "Web3Endpoint.h" + +using namespace bcos::rpc; + +Web3Endpoint::Web3Endpoint(bcos::rpc::GroupManager::Ptr groupManager) + : m_groupManager(std::move(groupManager)) +{} diff --git a/bcos-rpc/bcos-rpc/jsonrpc/endpoints/Web3Endpoint.h b/bcos-rpc/bcos-rpc/jsonrpc/endpoints/Web3Endpoint.h new file mode 100644 index 0000000000..55c720659e --- /dev/null +++ b/bcos-rpc/bcos-rpc/jsonrpc/endpoints/Web3Endpoint.h @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file Web3Endpoint.h + * @author: kyonGuo + * @date 2024/3/21 + */ + +#pragma once +#include "EndpointInterface.h" +#include "bcos-rpc/groupmgr/GroupManager.h" +#include +#include +#include +#include + +namespace bcos::rpc +{ +class Web3Endpoint : public EndpointInterface +{ +public: + explicit Web3Endpoint(bcos::rpc::GroupManager::Ptr groupManager); + std::string getEntryName() const override { return "web3"; } + MethodMap const& exportMethods() override { return m_methods; } + void clientVersion(RespFunc) {} + void sha3(RespFunc) {} + +private: + bcos::rpc::GroupManager::Ptr m_groupManager; +}; + +} // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-table/src/LegacyStorageWrapper.h b/bcos-table/src/LegacyStorageWrapper.h index cf256a18ae..6c82de205a 100644 --- a/bcos-table/src/LegacyStorageWrapper.h +++ b/bcos-table/src/LegacyStorageWrapper.h @@ -101,7 +101,7 @@ class LegacyStorageWrapper : public virtual bcos::storage::StorageInterface decltype(callback) callback) -> task::Task { try { - auto stateKeys = keys | RANGES::views::transform([&table](auto&& key) -> auto{ + auto stateKeys = keys | RANGES::views::transform([&table](auto&& key) -> auto { return transaction_executor::StateKeyView{ table, std::forward(key)}; }) | RANGES::to(); @@ -153,7 +153,7 @@ class LegacyStorageWrapper : public virtual bcos::storage::StorageInterface keys | RANGES::views::transform([&](std::string_view key) { return transaction_executor::StateKey{tableName, key}; }), - values | RANGES::views::transform([](std::string_view value) -> auto{ + values | RANGES::views::transform([](std::string_view value) -> auto { storage::Entry entry; entry.setField(0, value); return entry; diff --git a/bcos-tars-protocol/bcos-tars-protocol/tars/Transaction.tars b/bcos-tars-protocol/bcos-tars-protocol/tars/Transaction.tars index f5aaf1b2fa..101e407a39 100644 --- a/bcos-tars-protocol/bcos-tars-protocol/tars/Transaction.tars +++ b/bcos-tars-protocol/bcos-tars-protocol/tars/Transaction.tars @@ -25,5 +25,7 @@ module bcostars { // 6 optional string source; 7 optional vector sender; 8 optional string extraData; + 9 optional byte type; + 10 optional vector extraTransactionBytes; }; }; From 7d2e678d5d3c6c428989b5e6ef0141bb90babd53 Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Wed, 27 Mar 2024 21:35:40 +0800 Subject: [PATCH 02/39] (rpc,init): add web3 rpc service, add init web3 rpc logic. (#4324) --- bcos-rpc/bcos-rpc/Common.h | 50 ---- bcos-rpc/bcos-rpc/Rpc.cpp | 8 + bcos-rpc/bcos-rpc/Rpc.h | 14 ++ bcos-rpc/bcos-rpc/RpcFactory.cpp | 47 +++- bcos-rpc/bcos-rpc/RpcFactory.h | 7 + .../bcos-rpc/jsonrpc/JsonRpcInterface.cpp | 16 +- bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h | 15 +- .../jsonrpc/endpoints/EthEndpoint.cpp | 28 --- .../bcos-rpc/jsonrpc/endpoints/EthEndpoint.h | 85 ------- .../Web3JsonRpcImpl.cpp | 0 .../Web3JsonRpcImpl.h | 34 +-- .../endpoints/EndpointInterface.h | 7 +- .../web3jsonrpc/endpoints/EthEndpoint.cpp | 90 +++++++ .../web3jsonrpc/endpoints/EthEndpoint.h | 226 ++++++++++++++++++ .../web3jsonrpc/endpoints/EthMethods.h | 79 ++++++ .../endpoints/NetEndpoint.cpp | 0 .../endpoints/NetEndpoint.h | 4 +- .../endpoints/Web3Endpoint.cpp | 0 .../endpoints/Web3Endpoint.h | 4 +- bcos-tool/bcos-tool/NodeConfig.cpp | 25 ++ bcos-tool/bcos-tool/NodeConfig.h | 13 + cmake/CompilerSettings.cmake | 2 +- 22 files changed, 553 insertions(+), 201 deletions(-) delete mode 100644 bcos-rpc/bcos-rpc/jsonrpc/endpoints/EthEndpoint.cpp delete mode 100644 bcos-rpc/bcos-rpc/jsonrpc/endpoints/EthEndpoint.h rename bcos-rpc/bcos-rpc/{jsonrpc => web3jsonrpc}/Web3JsonRpcImpl.cpp (100%) rename bcos-rpc/bcos-rpc/{jsonrpc => web3jsonrpc}/Web3JsonRpcImpl.h (62%) rename bcos-rpc/bcos-rpc/{jsonrpc => web3jsonrpc}/endpoints/EndpointInterface.h (89%) create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthMethods.h rename bcos-rpc/bcos-rpc/{jsonrpc => web3jsonrpc}/endpoints/NetEndpoint.cpp (100%) rename bcos-rpc/bcos-rpc/{jsonrpc => web3jsonrpc}/endpoints/NetEndpoint.h (90%) rename bcos-rpc/bcos-rpc/{jsonrpc => web3jsonrpc}/endpoints/Web3Endpoint.cpp (100%) rename bcos-rpc/bcos-rpc/{jsonrpc => web3jsonrpc}/endpoints/Web3Endpoint.h (90%) diff --git a/bcos-rpc/bcos-rpc/Common.h b/bcos-rpc/bcos-rpc/Common.h index 83e861aeb1..f0a8cc023b 100644 --- a/bcos-rpc/bcos-rpc/Common.h +++ b/bcos-rpc/bcos-rpc/Common.h @@ -33,54 +33,4 @@ enum AMOPClientMessageType AMOP_BROADCAST = 0x112, // 274 AMOP_RESPONSE = 0x113 // 275 }; - -namespace eth -{ -constexpr std::string_view web3_clientVersion{"web3_clientVersion"}; -constexpr std::string_view web3_sha3{"web3_sha3"}; -constexpr std::string_view net_version{"net_version"}; -constexpr std::string_view net_listening{"net_listening"}; -constexpr std::string_view net_peerCount{"net_peerCount"}; -constexpr std::string_view eth_protocolVersion{"eth_protocolVersion"}; -constexpr std::string_view eth_syncing{"eth_syncing"}; -constexpr std::string_view eth_coinbase{"eth_coinbase"}; -constexpr std::string_view eth_chainId{"eth_chainId"}; -constexpr std::string_view eth_mining{"eth_mining"}; -constexpr std::string_view eth_hashrate{"eth_hashrate"}; -constexpr std::string_view eth_gasPrice{"eth_gasPrice"}; -constexpr std::string_view eth_accounts{"eth_accounts"}; -constexpr std::string_view eth_blockNumber{"eth_blockNumber"}; -constexpr std::string_view eth_getBalance{"eth_getBalance"}; -constexpr std::string_view eth_getStorageAt{"eth_getStorageAt"}; -constexpr std::string_view eth_getTransactionCount{"eth_getTransactionCount"}; -constexpr std::string_view eth_getBlockTxCountByHash{"eth_getBlockTransactionCountByHash"}; -constexpr std::string_view eth_getBlockTxCountByNumber{"eth_getBlockTransactionCountByNumber"}; -constexpr std::string_view eth_getUncleCountByBlockHash{"eth_getUncleCountByBlockHash"}; -constexpr std::string_view eth_getUncleCountByBlockNumber{"eth_getUncleCountByBlockNumber"}; -constexpr std::string_view eth_getCode{"eth_getCode"}; -constexpr std::string_view eth_sign{"eth_sign"}; -constexpr std::string_view eth_sendTransaction{"eth_sendTransaction"}; -constexpr std::string_view eth_signTransaction{"eth_signTransaction"}; -constexpr std::string_view eth_sendRawTransaction{"eth_sendRawTransaction"}; -constexpr std::string_view eth_call{"eth_call"}; -constexpr std::string_view eth_estimateGas{"eth_estimateGas"}; -constexpr std::string_view eth_getBlockByHash{"eth_getBlockByHash"}; -constexpr std::string_view eth_getBlockByNumber{"eth_getBlockByNumber"}; -constexpr std::string_view eth_getTransactionByHash{"eth_getTransactionByHash"}; -constexpr std::string_view eth_getTxByBlockHashAndIndex{"eth_getTransactionByBlockHashAndIndex"}; -constexpr std::string_view eth_getTxByBlockNumAndIndex{"eth_getTransactionByBlockNumberAndIndex"}; -constexpr std::string_view eth_getTransactionReceipt{"eth_getTransactionReceipt"}; -constexpr std::string_view eth_getUncleByBlockHashAndIndex{"eth_getUncleByBlockHashAndIndex"}; -constexpr std::string_view eth_getUncleByBlockNumberAndIndex{"eth_getUncleByBlockNumberAndIndex"}; -constexpr std::string_view eth_newFilter{"eth_newFilter"}; -constexpr std::string_view eth_newBlockFilter{"eth_newBlockFilter"}; -constexpr std::string_view eth_newPendingTransactionFilter{"eth_newPendingTransactionFilter"}; -constexpr std::string_view eth_uninstallFilter{"eth_uninstallFilter"}; -constexpr std::string_view eth_getFilterChanges{"eth_getFilterChanges"}; -constexpr std::string_view eth_getFilterLogs{"eth_getFilterLogs"}; -constexpr std::string_view eth_getLogs{"eth_getLogs"}; - -// constexpr std::string_view eth_createAccessList{"eth_createAccessList"}; -// constexpr std::string_view eth_maxPriorityFeePerGas{"eth_maxPriorityFeePerGas"}; -} // namespace eth } // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/Rpc.cpp b/bcos-rpc/bcos-rpc/Rpc.cpp index 89df9c9c01..0679696e64 100644 --- a/bcos-rpc/bcos-rpc/Rpc.cpp +++ b/bcos-rpc/bcos-rpc/Rpc.cpp @@ -74,6 +74,10 @@ void Rpc::start() // start websocket service m_wsService->start(); m_amopClient->start(); + if (m_web3Service) + { + m_web3Service->start(); + } RPC_LOG(INFO) << LOG_DESC("start rpc successfully"); } @@ -94,6 +98,10 @@ void Rpc::stop() { m_amopClient->stop(); } + if (m_web3Service) + { + m_web3Service->stop(); + } RPC_LOG(INFO) << LOG_DESC("[RPC][RPC][stop]") << LOG_DESC("stop rpc successfully"); } diff --git a/bcos-rpc/bcos-rpc/Rpc.h b/bcos-rpc/bcos-rpc/Rpc.h index af21fe3968..95216e5309 100644 --- a/bcos-rpc/bcos-rpc/Rpc.h +++ b/bcos-rpc/bcos-rpc/Rpc.h @@ -21,6 +21,8 @@ #pragma once #include "bcos-rpc/groupmgr/GroupManager.h" +#include "web3jsonrpc/Web3JsonRpcImpl.h" + #include #include #include @@ -85,6 +87,16 @@ class Rpc : public RPCInterface, public std::enable_shared_from_this m_amopClient->asyncNotifySubscribeTopic(_callback); } + void setWeb3Service(boostssl::ws::WsService::Ptr _web3Service) + { + m_web3Service = std::move(_web3Service); + } + + void setWeb3JsonRpcImpl(bcos::rpc::Web3JsonRpcImpl::Ptr _web3JsonRpcImpl) + { + m_web3JsonRpcImpl = std::move(_web3JsonRpcImpl); + } + GroupManager::Ptr groupManager() { return m_groupManager; } protected: @@ -102,6 +114,8 @@ class Rpc : public RPCInterface, public std::enable_shared_from_this bcos::event::EventSub::Ptr m_eventSub; AMOPClient::Ptr m_amopClient; GroupManager::Ptr m_groupManager; + boostssl::ws::WsService::Ptr m_web3Service = nullptr; + bcos::rpc::Web3JsonRpcImpl::Ptr m_web3JsonRpcImpl = nullptr; bcos::protocol::ProtocolInfo::ConstPtr m_localProtocol; }; diff --git a/bcos-rpc/bcos-rpc/RpcFactory.cpp b/bcos-rpc/bcos-rpc/RpcFactory.cpp index 1f1a3ab634..ce40f15a2a 100644 --- a/bcos-rpc/bcos-rpc/RpcFactory.cpp +++ b/bcos-rpc/bcos-rpc/RpcFactory.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -320,6 +319,24 @@ std::shared_ptr RpcFactory::initConfig( return wsConfig; } +std::shared_ptr RpcFactory::initWeb3RpcServiceConfig( + const bcos::tool::NodeConfig::Ptr& _nodeConfig) +{ + auto wsConfig = std::make_shared(); + wsConfig->setModel(bcos::boostssl::ws::WsModel::Server); + + wsConfig->setListenIP(_nodeConfig->web3RpcListenIP()); + wsConfig->setListenPort(_nodeConfig->web3RpcListenPort()); + wsConfig->setThreadPoolSize(_nodeConfig->web3RpcThreadSize()); + wsConfig->setDisableSsl(true); + RPC_LOG(INFO) << LOG_BADGE("initWeb3RpcServiceConfig") + << LOG_KV("listenIP", wsConfig->listenIP()) + << LOG_KV("listenPort", wsConfig->listenPort()) + << LOG_KV("threadCount", wsConfig->threadPoolSize()) + << LOG_KV("asServer", wsConfig->asServer()); + return wsConfig; +} + bcos::boostssl::ws::WsService::Ptr RpcFactory::buildWsService( bcos::boostssl::ws::WsConfig::Ptr _config) { @@ -336,9 +353,6 @@ bcos::boostssl::ws::WsService::Ptr RpcFactory::buildWsService( bcos::rpc::JsonRpcImpl_2_0::Ptr RpcFactory::buildJsonRpc(int sendTxTimeout, const std::shared_ptr& _wsService, GroupManager::Ptr _groupManager) { - // auto web3Rpc = std::make_shared(_groupManager, m_gateway, - // _wsService); - // JsonRpcImpl_2_0 //* auto jsonRpcInterface = @@ -353,6 +367,22 @@ bcos::rpc::JsonRpcImpl_2_0::Ptr RpcFactory::buildJsonRpc(int sendTxTimeout, return jsonRpcInterface; } +bcos::rpc::Web3JsonRpcImpl::Ptr RpcFactory::buildWeb3JsonRpc( + int sendTxTimeout, boostssl::ws::WsService::Ptr _wsService, GroupManager::Ptr _groupManager) +{ + auto web3JsonRpc = + std::make_shared(std::move(_groupManager), m_gateway, _wsService); + web3JsonRpc->setSendTxTimeout(sendTxTimeout); + auto httpServer = _wsService->httpServer(); + if (httpServer) + { + httpServer->setHttpReqHandler(std::bind(&bcos::rpc::Web3JsonRpcImpl::onRPCRequest, + web3JsonRpc, std::placeholders::_1, std::placeholders::_2)); + } + return web3JsonRpc; +} + + bcos::event::EventSub::Ptr RpcFactory::buildEventSub( const std::shared_ptr& _wsService, GroupManager::Ptr _groupManager) { @@ -391,6 +421,15 @@ Rpc::Ptr RpcFactory::buildLocalRpc( auto groupManager = buildAirGroupManager(_groupInfo, _nodeService); auto amopClient = buildAirAMOPClient(wsService); auto rpc = buildRpc(m_nodeConfig->sendTxTimeout(), wsService, groupManager, amopClient); + if (m_nodeConfig->enableWeb3Rpc()) + { + auto web3Config = initWeb3RpcServiceConfig(m_nodeConfig); + auto web3WsService = buildWsService(std::move(web3Config)); + auto web3JsonRpc = + buildWeb3JsonRpc(m_nodeConfig->sendTxTimeout(), web3WsService, groupManager); + rpc->setWeb3Service(std::move(web3WsService)); + rpc->setWeb3JsonRpcImpl(std::move(web3JsonRpc)); + } // Note: init groupManager after create rpc and register the handlers groupManager->init(); return rpc; diff --git a/bcos-rpc/bcos-rpc/RpcFactory.h b/bcos-rpc/bcos-rpc/RpcFactory.h index 523e9a0381..a7ac40185a 100644 --- a/bcos-rpc/bcos-rpc/RpcFactory.h +++ b/bcos-rpc/bcos-rpc/RpcFactory.h @@ -24,6 +24,8 @@ #include "bcos-rpc/amop/AirAMOPClient.h" #include "bcos-rpc/groupmgr/AirGroupManager.h" #include "bcos-rpc/groupmgr/GroupManager.h" +#include "web3jsonrpc/Web3JsonRpcImpl.h" + #include #include #include @@ -57,6 +59,8 @@ class RpcFactory : public std::enable_shared_from_this std::shared_ptr initConfig( const bcos::tool::NodeConfig::Ptr& _nodeConfig); + std::shared_ptr initWeb3RpcServiceConfig( + const bcos::tool::NodeConfig::Ptr& _nodeConfig); std::shared_ptr buildWsService( bcos::boostssl::ws::WsConfig::Ptr _config); @@ -86,6 +90,9 @@ class RpcFactory : public std::enable_shared_from_this bcos::rpc::JsonRpcImpl_2_0::Ptr buildJsonRpc(int sendTxTimeout, const std::shared_ptr& _wsService, GroupManager::Ptr _groupManager); + + bcos::rpc::Web3JsonRpcImpl::Ptr buildWeb3JsonRpc(int sendTxTimeout, + boostssl::ws::WsService::Ptr _wsService, GroupManager::Ptr _groupManager); bcos::event::EventSub::Ptr buildEventSub( const std::shared_ptr& _wsService, GroupManager::Ptr _groupManager); diff --git a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.cpp b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.cpp index e3b3bbfc8b..157a81799a 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.cpp +++ b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.cpp @@ -91,7 +91,10 @@ void JsonRpcInterface::onRPCRequest(std::string_view _requestBody, Sender _sende BOOST_THROW_EXCEPTION(JsonRpcException( JsonRpcError::MethodNotFound, "The method does not exist/is not available.")); } - RPC_IMPL_LOG(TRACE) << LOG_BADGE("onRPCRequest") << LOG_KV("request", _requestBody); + if (c_fileLogLevel == TRACE) [[unlikely]] + { + RPC_IMPL_LOG(TRACE) << LOG_BADGE("onRPCRequest") << LOG_KV("request", _requestBody); + } it->second( request.params, [response, _sender](Error::Ptr _error, Json::Value& _result) mutable { if (_error && (_error->errorCode() != bcos::protocol::CommonError::SUCCESS)) @@ -105,10 +108,13 @@ void JsonRpcInterface::onRPCRequest(std::string_view _requestBody, Sender _sende response.result.swap(_result); } auto strResp = toStringResponse(std::move(response)); - RPC_IMPL_LOG(TRACE) - << LOG_BADGE("onRPCRequest") - << LOG_KV("response", - std::string_view((const char*)strResp.data(), strResp.size())); + if (c_fileLogLevel == TRACE) [[unlikely]] + { + RPC_IMPL_LOG(TRACE) + << LOG_BADGE("onRPCRequest") + << LOG_KV("response", + std::string_view((const char*)strResp.data(), strResp.size())); + } _sender(std::move(strResp)); }); diff --git a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h index a25ee0a5ec..24a0c2fe4c 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h +++ b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h @@ -31,9 +31,19 @@ namespace bcos::rpc { +struct string_hash +{ + using hash_type = std::hash; + using is_transparent = void; + + std::size_t operator()(const char* str) const { return hash_type{}(str); } + std::size_t operator()(std::string_view str) const { return hash_type{}(str); } + std::size_t operator()(std::string const& str) const { return hash_type{}(str); } +}; using Sender = std::function; using RespFunc = std::function; -using MethodMap = std::unordered_map>; +using MethodMap = std::unordered_map, + string_hash, std::equal_to<>>; class JsonRpcInterface { @@ -120,13 +130,12 @@ class JsonRpcInterface virtual void getGroupBlockNumber(RespFunc _respFunc) = 0; -public: void onRPCRequest(std::string_view _requestBody, Sender _sender); protected: void initMethod(); - std::unordered_map> m_methodToFunc; + MethodMap m_methodToFunc; std::string_view toView(const Json::Value& value) diff --git a/bcos-rpc/bcos-rpc/jsonrpc/endpoints/EthEndpoint.cpp b/bcos-rpc/bcos-rpc/jsonrpc/endpoints/EthEndpoint.cpp deleted file mode 100644 index 0af60476a1..0000000000 --- a/bcos-rpc/bcos-rpc/jsonrpc/endpoints/EthEndpoint.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (C) 2024 FISCO BCOS. - * SPDX-License-Identifier: Apache-2.0 - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @file EthEndpoint.cpp - * @author: kyonGuo - * @date 2024/3/21 - */ - -#include "EthEndpoint.h" - -using namespace bcos; -using namespace bcos::rpc; - -EthEndpoint::EthEndpoint(bcos::rpc::GroupManager::Ptr groupManager) - : m_groupManager(std::move(groupManager)) -{} diff --git a/bcos-rpc/bcos-rpc/jsonrpc/endpoints/EthEndpoint.h b/bcos-rpc/bcos-rpc/jsonrpc/endpoints/EthEndpoint.h deleted file mode 100644 index af7ab4727e..0000000000 --- a/bcos-rpc/bcos-rpc/jsonrpc/endpoints/EthEndpoint.h +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright (C) 2024 FISCO BCOS. - * SPDX-License-Identifier: Apache-2.0 - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @file EthEndpoint.h - * @author: kyonGuo - * @date 2024/3/21 - */ - -#pragma once -#include "bcos-rpc/groupmgr/GroupManager.h" -#include "bcos-rpc/jsonrpc/endpoints/EndpointInterface.h" -#include -#include -#include -#include -#include - -namespace bcos::rpc -{ - -/** - * eth entry point to match 'eth_' methods - */ -class EthEndpoint : public EndpointInterface -{ -public: - explicit EthEndpoint(bcos::rpc::GroupManager::Ptr group_manager); - std::string getEntryName() const override { return "eth"; } - MethodMap const& exportMethods() override { return m_methods; } - void protoclVersion(RespFunc) {} - void syning(RespFunc) {} - void coinbase(RespFunc) {} - void chainId(RespFunc) {} - void mining(RespFunc) {} - void hashrate(RespFunc) {} - void gasPrice(RespFunc) {} - void accounts(RespFunc) {} - void blockNumber(RespFunc) {} - void getBalance(std::string_view, std::string_view, RespFunc) {} - void getStorageAt(std::string_view, std::string_view, std::string_view, RespFunc) {} - void getTransactionCount(std::string_view, std::string_view, RespFunc) {} - void getBlockTxCountByHash(std::string_view, RespFunc) {} - void getBlockTxCountByNumber(std::string_view, RespFunc) {} - void getUncleCountByBlockHash(std::string_view, RespFunc) {} - void getUncleCountByBlockNumber(std::string_view, RespFunc) {} - void getCode(std::string_view, std::string_view, RespFunc) {} - void sign(std::string_view, std::string_view, RespFunc) {} - void signTransaction(Json::Value const&, RespFunc) {} - void sendTransaction(Json::Value const&, RespFunc) {} - void sendRawTransaction(std::string_view, RespFunc) {} - void call(Json::Value const&, std::string_view, RespFunc) {} - void estimateGas(Json::Value const&, std::string_view, RespFunc) {} - void getBlockByHash(std::string_view, bool, RespFunc) {} - void getBlockByNumber(std::string_view, bool, RespFunc) {} - void getTransactionByHash(std::string_view, RespFunc) {} - void getTransactionByBlockHashAndIndex(std::string_view, std::string_view, RespFunc) {} - void getTransactionByBlockNumberAndIndex(std::string_view, std::string_view, RespFunc) {} - void getTransactionReceipt(std::string_view, RespFunc) {} - void getUncleByBlockHashAndIndex(std::string_view, std::string_view, RespFunc) {} - void getUncleByBlockNumberAndIndex(std::string_view, std::string_view, RespFunc) {} - void newFilter(Json::Value const&, RespFunc) {} - void newBlockFilter(RespFunc) {} - void newPendingTransactionFilter(RespFunc) {} - void uninstallFilter(std::string_view, RespFunc) {} - void getFilterChanges(std::string_view, RespFunc) {} - void getFilterLogs(std::string_view, RespFunc) {} - void getLogs(Json::Value const&, RespFunc) {} - -private: - bcos::rpc::GroupManager::Ptr m_groupManager; -}; - -} // namespace bcos::rpc diff --git a/bcos-rpc/bcos-rpc/jsonrpc/Web3JsonRpcImpl.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp similarity index 100% rename from bcos-rpc/bcos-rpc/jsonrpc/Web3JsonRpcImpl.cpp rename to bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp diff --git a/bcos-rpc/bcos-rpc/jsonrpc/Web3JsonRpcImpl.h b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h similarity index 62% rename from bcos-rpc/bcos-rpc/jsonrpc/Web3JsonRpcImpl.h rename to bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h index 49cceb33cd..18ea6656be 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/Web3JsonRpcImpl.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h @@ -21,11 +21,11 @@ #pragma once #include "bcos-rpc/groupmgr/GroupManager.h" #include "bcos-rpc/jsonrpc/JsonRpcImpl_2_0.h" -#include "bcos-rpc/jsonrpc/endpoints/EndpointInterface.h" -#include "bcos-rpc/jsonrpc/endpoints/EthEndpoint.h" -#include "bcos-rpc/jsonrpc/endpoints/NetEndpoint.h" -#include "bcos-rpc/jsonrpc/endpoints/Web3Endpoint.h" #include "bcos-rpc/validator/CallValidator.h" +#include "bcos-rpc/web3jsonrpc/endpoints/EndpointInterface.h" +#include "bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h" +#include "bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.h" +#include "bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.h" #include #include #include @@ -42,27 +42,27 @@ class Web3Endpoint; class Web3JsonRpcImpl : public JsonRpcImpl_2_0, public std::enable_shared_from_this { public: - Web3JsonRpcImpl(GroupManager::Ptr _groupManager, + Web3JsonRpcImpl(bcos::rpc::GroupManager::Ptr _groupManager, bcos::gateway::GatewayInterface::Ptr _gatewayInterface, std::shared_ptr _wsService) : JsonRpcImpl_2_0( std::move(_groupManager), std::move(_gatewayInterface), std::move(_wsService)), - m_ethEntryPoint(std::make_unique(m_groupManager)), - m_netEntryPoint(std::make_unique(m_groupManager)), - m_web3EntryPoint(std::make_unique(m_groupManager)) + m_ethEndpoint(std::make_unique(m_groupManager)), + m_netEndpoint(std::make_unique(m_groupManager)), + m_web3Endpoint(std::make_unique(m_groupManager)) { - m_methodToFunc.insert( - m_ethEntryPoint->exportMethods().begin(), m_ethEntryPoint->exportMethods().end()); - m_methodToFunc.insert( - m_netEntryPoint->exportMethods().begin(), m_netEntryPoint->exportMethods().end()); - m_methodToFunc.insert( - m_web3EntryPoint->exportMethods().begin(), m_web3EntryPoint->exportMethods().end()); + auto&& ethMap = m_ethEndpoint->exportMethods(); + auto&& netMap = m_netEndpoint->exportMethods(); + auto&& web3Map = m_web3Endpoint->exportMethods(); + m_methodToFunc.insert(ethMap.begin(), ethMap.end()); + m_methodToFunc.insert(netMap.begin(), netMap.end()); + m_methodToFunc.insert(web3Map.begin(), web3Map.end()); } ~Web3JsonRpcImpl() override = default; private: - EthEndpoint::UniquePtr m_ethEntryPoint; - NetEndpoint::UniquePtr m_netEntryPoint; - Web3Endpoint::UniquePtr m_web3EntryPoint; + EthEndpoint::UniquePtr m_ethEndpoint; + NetEndpoint::UniquePtr m_netEndpoint; + Web3Endpoint::UniquePtr m_web3Endpoint; }; } // namespace bcos::rpc diff --git a/bcos-rpc/bcos-rpc/jsonrpc/endpoints/EndpointInterface.h b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointInterface.h similarity index 89% rename from bcos-rpc/bcos-rpc/jsonrpc/endpoints/EndpointInterface.h rename to bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointInterface.h index 9813108c68..48a6714b0c 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/endpoints/EndpointInterface.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointInterface.h @@ -20,8 +20,9 @@ #pragma once -#include "bcos-rpc/groupmgr/GroupManager.h" +#include #include +#include namespace bcos::rpc { @@ -35,9 +36,7 @@ class EndpointInterface EndpointInterface& operator=(const EndpointInterface&) = delete; EndpointInterface(EndpointInterface&&) = delete; EndpointInterface& operator=(EndpointInterface&&) = delete; - - virtual std::string getEntryName() const = 0; - virtual MethodMap const& exportMethods() = 0; + virtual MethodMap&& exportMethods() = 0; protected: MethodMap m_methods; diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp new file mode 100644 index 0000000000..a3677c76db --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp @@ -0,0 +1,90 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file EthEndpoint.cpp + * @author: kyonGuo + * @date 2024/3/21 + */ + +#include "EthEndpoint.h" + +#include +#include + +using namespace bcos; +using namespace bcos::rpc; + +EthEndpoint::EthEndpoint(bcos::rpc::GroupManager::Ptr groupManager) + : m_groupManager(std::move(groupManager)) +{ + initMethod(); +} + +void EthEndpoint::initMethod() +{ + // clang-format off + //m_methods[methodString(EthMethod::eth_protocolVersion)] = std::bind(&EthEndpoint::protocolVersionInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_syncing)] = std::bind(&EthEndpoint::syningInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_coinbase)] = std::bind(&EthEndpoint::coinbaseInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_chainId)] = std::bind(&EthEndpoint::chainIdInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_mining)] = std::bind(&EthEndpoint::miningInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_hashrate)] = std::bind(&EthEndpoint::hashrateInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_gasPrice)] = std::bind(&EthEndpoint::gasPriceInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_accounts)] = std::bind(&EthEndpoint::accountsInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_blockNumber)] = std::bind(&EthEndpoint::blockNumberInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_getBalance)] = std::bind(&EthEndpoint::getBalanceInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_getStorageAt)] = std::bind(&EthEndpoint::getStorageAtInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_getTransactionCount)] = std::bind(&EthEndpoint::getTransactionCountInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_getBlockTransactionCountByHash)] = std::bind(&EthEndpoint::getBlockTxCountByHashInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_getBlockTransactionCountByNumber)] = std::bind(&EthEndpoint::getBlockTxCountByNumberInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_getUncleCountByBlockHash)] = std::bind(&EthEndpoint::getUncleCountByBlockHashInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_getUncleCountByBlockNumber)] = std::bind(&EthEndpoint::getUncleCountByBlockNumberInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_getCode)] = std::bind(&EthEndpoint::getCodeInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_sign)] = std::bind(&EthEndpoint::signInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_sendTransaction)] = std::bind(&EthEndpoint::sendTransactionInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_signTransaction)] = std::bind(&EthEndpoint::signTransactionInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_sendRawTransaction)] = std::bind(&EthEndpoint::sendRawTransactionInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_call)] = std::bind(&EthEndpoint::callInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_estimateGas)] = std::bind(&EthEndpoint::estimateGasInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_getBlockByHash)] = std::bind(&EthEndpoint::getBlockByHashInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_getBlockByNumber)] = std::bind(&EthEndpoint::getBlockByNumberInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_getTransactionByHash)] = std::bind(&EthEndpoint::getTransactionByHashInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_getTransactionByBlockHashAndIndex)] = std::bind(&EthEndpoint::getTransactionByBlockHashAndIndexInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_getTransactionByBlockNumberAndIndex)] = std::bind(&EthEndpoint::getTransactionByBlockNumberAndIndexInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_getTransactionReceipt)] = std::bind(&EthEndpoint::getTransactionReceiptInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_getUncleByBlockHashAndIndex)] = std::bind(&EthEndpoint::getUncleByBlockHashAndIndexInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_getUncleByBlockNumberAndIndex)] = std::bind(&EthEndpoint::getUncleByBlockNumberAndIndexInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_newFilter)] = std::bind(&EthEndpoint::newFilterInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_newBlockFilter)] = std::bind(&EthEndpoint::newBlockFilterInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_newPendingTransactionFilter)] = std::bind(&EthEndpoint::newPendingTransactionFilterInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_uninstallFilter)] = std::bind(&EthEndpoint::uninstallFilterInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_getFilterChanges)] = std::bind(&EthEndpoint::getFilterChangesInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_getFilterLogs)] = std::bind(&EthEndpoint::getFilterLogsInterface, this, std::placeholders::_1, std::placeholders::_2); + //m_methods[methodString(EthMethod::eth_getLogs)] = std::bind(&EthEndpoint::getLogsInterface, this, std::placeholders::_1, std::placeholders::_2); + // clang-format on + for (auto& [method, _] : m_methods) + { + RPC_IMPL_LOG(INFO) << LOG_BADGE("initMethod") << LOG_KV("method", method); + } + RPC_IMPL_LOG(INFO) << LOG_BADGE("initMethod") << LOG_KV("size", m_methods.size()); +} + +// TODO: error code + +void EthEndpoint::protocolVersion(RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(-1, "This API has not been implemented yet!"), result); +} diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h new file mode 100644 index 0000000000..ab47d90b84 --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h @@ -0,0 +1,226 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file EthEndpoint.h + * @author: kyonGuo + * @date 2024/3/21 + */ + +#pragma once +#include "EndpointInterface.h" +#include "bcos-rpc/groupmgr/GroupManager.h" +#include +#include +#include +#include +#include + +namespace bcos::rpc +{ + +/** + * eth entry point to match 'eth_' methods + */ +class EthEndpoint : public EndpointInterface +{ +public: + explicit EthEndpoint(bcos::rpc::GroupManager::Ptr group_manager); + void initMethod(); + MethodMap&& exportMethods() override { return std::move(m_methods); } + void protocolVersion(RespFunc); + void syning(RespFunc) {} + void coinbase(RespFunc) {} + void chainId(RespFunc) {} + void mining(RespFunc) {} + void hashrate(RespFunc) {} + void gasPrice(RespFunc) {} + void accounts(RespFunc) {} + void blockNumber(RespFunc) {} + void getBalance(std::string_view, std::string_view, RespFunc) {} + void getStorageAt(std::string_view, std::string_view, std::string_view, RespFunc) {} + void getTransactionCount(std::string_view, std::string_view, RespFunc) {} + void getBlockTxCountByHash(std::string_view, RespFunc) {} + void getBlockTxCountByNumber(std::string_view, RespFunc) {} + void getUncleCountByBlockHash(std::string_view, RespFunc) {} + void getUncleCountByBlockNumber(std::string_view, RespFunc) {} + void getCode(std::string_view, std::string_view, RespFunc) {} + void sign(std::string_view, std::string_view, RespFunc) {} + void signTransaction(Json::Value const&, RespFunc) {} + void sendTransaction(Json::Value const&, RespFunc) {} + void sendRawTransaction(std::string_view, RespFunc) {} + void call(Json::Value const&, std::string_view, RespFunc) {} + void estimateGas(Json::Value const&, std::string_view, RespFunc) {} + void getBlockByHash(std::string_view, bool, RespFunc) {} + void getBlockByNumber(std::string_view, bool, RespFunc) {} + void getTransactionByHash(std::string_view, RespFunc) {} + void getTransactionByBlockHashAndIndex(std::string_view, std::string_view, RespFunc) {} + void getTransactionByBlockNumberAndIndex(std::string_view, std::string_view, RespFunc) {} + void getTransactionReceipt(std::string_view, RespFunc) {} + void getUncleByBlockHashAndIndex(std::string_view, std::string_view, RespFunc) {} + void getUncleByBlockNumberAndIndex(std::string_view, std::string_view, RespFunc) {} + void newFilter(Json::Value const&, RespFunc) {} + void newBlockFilter(RespFunc) {} + void newPendingTransactionFilter(RespFunc) {} + void uninstallFilter(std::string_view, RespFunc) {} + void getFilterChanges(std::string_view, RespFunc) {} + void getFilterLogs(std::string_view, RespFunc) {} + void getLogs(Json::Value const&, RespFunc) {} + +protected: + std::string_view toView(const Json::Value& value) + { + const char* begin = nullptr; + const char* end = nullptr; + if (!value.getString(&begin, &end)) + { + return {}; + } + std::string_view view(begin, end - begin); + return view; + } + // void protocolVersionInterface(Json::Value const&, RespFunc func) + //{ + // protocolVersion(std::move(func)); + // } + // void syningInterface(Json::Value const&, RespFunc func) { syning(std::move(func)); } + // void coinbaseInterface(Json::Value const&, RespFunc func) { coinbase(std::move(func)); } + // void chainIdInterface(Json::Value const&, RespFunc func) { chainId(std::move(func)); } + // void miningInterface(Json::Value const&, RespFunc func) { mining(std::move(func)); } + // void hashrateInterface(Json::Value const&, RespFunc func) { hashrate(std::move(func)); } + // void gasPriceInterface(Json::Value const&, RespFunc func) { gasPrice(std::move(func)); } + // void accountsInterface(Json::Value const&, RespFunc func) { accounts(std::move(func)); } + // void blockNumberInterface(Json::Value const&, RespFunc func) { blockNumber(std::move(func)); + // } void getBalanceInterface(Json::Value const& req, RespFunc func) + //{ + // getBalance(toView(req[0U]), toView(req[1U]), std::move(func)); + // } + // void getStorageAtInterface(Json::Value const& req, RespFunc func) + //{ + // getStorageAt(toView(req[0U]), toView(req[1U]), toView(req[2U]), std::move(func)); + // } + // void getTransactionCountInterface(Json::Value const& req, RespFunc func) + //{ + // getTransactionCount(toView(req[0U]), toView(req[1U]), std::move(func)); + // } + // void getBlockTxCountByHashInterface(Json::Value const& req, RespFunc func) + //{ + // getBlockTxCountByHash(toView(req[0U]), std::move(func)); + // } + // void getBlockTxCountByNumberInterface(Json::Value const& req, RespFunc func) + //{ + // getBlockTxCountByNumber(toView(req[0U]), std::move(func)); + // } + // void getUncleCountByBlockHashInterface(Json::Value const& req, RespFunc func) + //{ + // getUncleCountByBlockHash(toView(req[0U]), std::move(func)); + // } + // void getUncleCountByBlockNumberInterface(Json::Value const& req, RespFunc func) + //{ + // getUncleCountByBlockNumber(toView(req[0U]), std::move(func)); + // } + // void getCodeInterface(Json::Value const& req, RespFunc func) + //{ + // getCode(toView(req[0U]), toView(req[1U]), std::move(func)); + // } + // void signInterface(Json::Value const& req, RespFunc func) + //{ + // sign(toView(req[0U]), toView(req[1U]), std::move(func)); + // } + // void signTransactionInterface(Json::Value const& req, RespFunc func) + //{ + // signTransaction(req[0U], std::move(func)); + // } + // void sendTransactionInterface(Json::Value const& req, RespFunc func) + //{ + // sendTransaction(req[0U], std::move(func)); + // } + // void sendRawTransactionInterface(Json::Value const& req, RespFunc func) + //{ + // sendRawTransaction(toView(req[0U]), std::move(func)); + // } + // void callInterface(Json::Value const& req, RespFunc func) + //{ + // call(req[0U], toView(req[1U]), std::move(func)); + // } + // void estimateGasInterface(Json::Value const& req, RespFunc func) + //{ + // estimateGas(req[0U], toView(req[1U]), std::move(func)); + // } + // void getBlockByHashInterface(Json::Value const& req, RespFunc func) + //{ + // getBlockByHash(toView(req[0U]), req[1U].asBool(), std::move(func)); + // } + // void getBlockByNumberInterface(Json::Value const& req, RespFunc func) + //{ + // getBlockByNumber(toView(req[0U]), req[1U].asBool(), std::move(func)); + // } + // void getTransactionByHashInterface(Json::Value const& req, RespFunc func) + //{ + // getTransactionByHash(toView(req[0U]), std::move(func)); + // } + // void getTransactionByBlockHashAndIndexInterface(Json::Value const& req, RespFunc func) + //{ + // getTransactionByBlockHashAndIndex(toView(req[0U]), toView(req[1U]), std::move(func)); + // } + // void getTransactionByBlockNumberAndIndexInterface(Json::Value const& req, RespFunc func) + //{ + // getTransactionByBlockNumberAndIndex(toView(req[0U]), toView(req[1U]), std::move(func)); + // } + // void getTransactionReceiptInterface(Json::Value const& req, RespFunc func) + //{ + // getTransactionReceipt(toView(req[0U]), std::move(func)); + // } + // void getUncleByBlockHashAndIndexInterface(Json::Value const& req, RespFunc func) + //{ + // getUncleByBlockHashAndIndex(toView(req[0U]), toView(req[1U]), std::move(func)); + // } + // void getUncleByBlockNumberAndIndexInterface(Json::Value const& req, RespFunc func) + //{ + // getUncleByBlockNumberAndIndex(toView(req[0U]), toView(req[1U]), std::move(func)); + // } + // void newFilterInterface(Json::Value const& req, RespFunc func) + //{ + // newFilter(req[0], std::move(func)); + // } + // void newBlockFilterInterface(Json::Value const&, RespFunc func) + //{ + // newBlockFilter(std::move(func)); + // } + // void newPendingTransactionFilterInterface(Json::Value const&, RespFunc func) + //{ + // newPendingTransactionFilter(std::move(func)); + // } + // void uninstallFilterInterface(Json::Value const& req, RespFunc func) + //{ + // uninstallFilter(toView(req[0U]), std::move(func)); + // } + // void getFilterChangesInterface(Json::Value const& req, RespFunc func) + //{ + // getFilterChanges(toView(req[0U]), std::move(func)); + // } + // void getFilterLogsInterface(Json::Value const& req, RespFunc func) + //{ + // getFilterLogs(toView(req[0U]), std::move(func)); + // } + // void getLogsInterface(Json::Value const& req, RespFunc func) + //{ + // getLogs(req[0U], std::move(func)); + // } + +private: + bcos::rpc::GroupManager::Ptr m_groupManager; +}; + +} // namespace bcos::rpc diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthMethods.h b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthMethods.h new file mode 100644 index 0000000000..f6fb3420a2 --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthMethods.h @@ -0,0 +1,79 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file EthMethods.h + * @author: kyonGuo + * @date 2024/3/27 + */ + +#pragma once + +#include +#include +namespace bcos::rpc +{ +enum class EthMethod +{ + web3_clientVersion, + web3_sha3, + net_version, + net_listening, + net_peerCount, + eth_protocolVersion, + eth_syncing, + eth_coinbase, + eth_chainId, + eth_mining, + eth_hashrate, + eth_gasPrice, + eth_accounts, + eth_blockNumber, + eth_getBalance, + eth_getStorageAt, + eth_getTransactionCount, + eth_getBlockTransactionCountByHash, + eth_getBlockTransactionCountByNumber, + eth_getUncleCountByBlockHash, + eth_getUncleCountByBlockNumber, + eth_getCode, + eth_sign, + eth_sendTransaction, + eth_signTransaction, + eth_sendRawTransaction, + eth_call, + eth_estimateGas, + eth_getBlockByHash, + eth_getBlockByNumber, + eth_getTransactionByHash, + eth_getTransactionByBlockHashAndIndex, + eth_getTransactionByBlockNumberAndIndex, + eth_getTransactionReceipt, + eth_getUncleByBlockHashAndIndex, + eth_getUncleByBlockNumberAndIndex, + eth_newFilter, + eth_newBlockFilter, + eth_newPendingTransactionFilter, + eth_uninstallFilter, + eth_getFilterChanges, + eth_getFilterLogs, + eth_getLogs +}; + +[[maybe_unused]] static std::string methodString(EthMethod _method) +{ + return std::string(magic_enum::enum_name(_method)); +} + +} // namespace bcos::rpc diff --git a/bcos-rpc/bcos-rpc/jsonrpc/endpoints/NetEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp similarity index 100% rename from bcos-rpc/bcos-rpc/jsonrpc/endpoints/NetEndpoint.cpp rename to bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp diff --git a/bcos-rpc/bcos-rpc/jsonrpc/endpoints/NetEndpoint.h b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.h similarity index 90% rename from bcos-rpc/bcos-rpc/jsonrpc/endpoints/NetEndpoint.h rename to bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.h index c54720d5b3..9fce1a1f63 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/endpoints/NetEndpoint.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.h @@ -33,8 +33,8 @@ class NetEndpoint : public EndpointInterface { public: explicit NetEndpoint(bcos::rpc::GroupManager::Ptr groupManager); - std::string getEntryName() const override { return "net"; } - MethodMap const& exportMethods() override { return m_methods; } + void initMethod(); + MethodMap&& exportMethods() override { return std::move(m_methods); } void verison(RespFunc) {} void listening(RespFunc) {} void peerCount(RespFunc) {} diff --git a/bcos-rpc/bcos-rpc/jsonrpc/endpoints/Web3Endpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp similarity index 100% rename from bcos-rpc/bcos-rpc/jsonrpc/endpoints/Web3Endpoint.cpp rename to bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp diff --git a/bcos-rpc/bcos-rpc/jsonrpc/endpoints/Web3Endpoint.h b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.h similarity index 90% rename from bcos-rpc/bcos-rpc/jsonrpc/endpoints/Web3Endpoint.h rename to bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.h index 55c720659e..18b17831c4 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/endpoints/Web3Endpoint.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.h @@ -32,8 +32,8 @@ class Web3Endpoint : public EndpointInterface { public: explicit Web3Endpoint(bcos::rpc::GroupManager::Ptr groupManager); - std::string getEntryName() const override { return "web3"; } - MethodMap const& exportMethods() override { return m_methods; } + void initMethod(); + MethodMap&& exportMethods() override { return std::move(m_methods); } void clientVersion(RespFunc) {} void sha3(RespFunc) {} diff --git a/bcos-tool/bcos-tool/NodeConfig.cpp b/bcos-tool/bcos-tool/NodeConfig.cpp index f9a13070a4..69d4631bed 100644 --- a/bcos-tool/bcos-tool/NodeConfig.cpp +++ b/bcos-tool/bcos-tool/NodeConfig.cpp @@ -69,6 +69,7 @@ void NodeConfig::loadConfig(boost::property_tree::ptree const& _pt, bool _enforc } loadCertConfig(_pt); loadRpcConfig(_pt); + loadWeb3RpcConfig(_pt); loadGatewayConfig(_pt); loadSealerConfig(_pt); loadTxPoolConfig(_pt); @@ -379,6 +380,30 @@ void NodeConfig::loadRpcConfig(boost::property_tree::ptree const& _pt) << LOG_KV("needRetInput", needRetInput); } +void NodeConfig::loadWeb3RpcConfig(boost::property_tree::ptree const& _pt) +{ + /* + [web3_rpc] + enble=false + listen_ip=0.0.0.0 + listen_port=8545 + thread_count=16 + */ + const std::string listenIP = _pt.get("web3_rpc.listen_ip", "0.0.0.0"); + const int listenPort = _pt.get("web3_rpc.listen_port", 8545); + const int threadCount = _pt.get("web3_rpc.thread_count", 8); + const bool enableWeb3Rpc = _pt.get("web3_rpc.enable", false); + + m_web3RpcListenIP = listenIP; + m_web3RpcListenPort = listenPort; + m_web3RpcThreadSize = threadCount; + m_enableWeb3Rpc = enableWeb3Rpc; + + NodeConfig_LOG(INFO) << LOG_DESC("loadWeb3RpcConfig") << LOG_KV("enableWeb3Rpc", enableWeb3Rpc) + << LOG_KV("listenIP", listenIP) << LOG_KV("listenPort", listenPort) + << LOG_KV("listenPort", listenPort); +} + void NodeConfig::loadGatewayConfig(boost::property_tree::ptree const& _pt) { /* diff --git a/bcos-tool/bcos-tool/NodeConfig.h b/bcos-tool/bcos-tool/NodeConfig.h index 9ded556fa6..1e6e35cbd4 100644 --- a/bcos-tool/bcos-tool/NodeConfig.h +++ b/bcos-tool/bcos-tool/NodeConfig.h @@ -180,6 +180,12 @@ class NodeConfig bool rpcSmSsl() const { return m_rpcSmSsl; } bool rpcDisableSsl() const { return m_rpcDisableSsl; } + // the web3 rpc configurations + bool enableWeb3Rpc() const { return m_enableWeb3Rpc; } + const std::string& web3RpcListenIP() const { return m_web3RpcListenIP; } + uint16_t web3RpcListenPort() const { return m_web3RpcListenPort; } + uint32_t web3RpcThreadSize() const { return m_web3RpcThreadSize; } + // the gateway configurations const std::string& p2pListenIP() const { return m_p2pListenIP; } uint16_t p2pListenPort() const { return m_p2pListenPort; } @@ -275,6 +281,7 @@ class NodeConfig protected: virtual void loadChainConfig(boost::property_tree::ptree const& _pt, bool _enforceGroupId); virtual void loadRpcConfig(boost::property_tree::ptree const& _pt); + virtual void loadWeb3RpcConfig(boost::property_tree::ptree const& _pt); virtual void loadGatewayConfig(boost::property_tree::ptree const& _pt); virtual void loadCertConfig(boost::property_tree::ptree const& _pt); virtual void loadTxPoolConfig(boost::property_tree::ptree const& _pt); @@ -418,6 +425,12 @@ class NodeConfig bool m_rpcSmSsl; bool m_rpcDisableSsl = false; + // config fro web3 rpc + bool m_enableWeb3Rpc = false; + std::string m_web3RpcListenIP; + uint16_t m_web3RpcListenPort; + uint32_t m_web3RpcThreadSize; + // config for gateway std::string m_p2pListenIP; uint16_t m_p2pListenPort; diff --git a/cmake/CompilerSettings.cmake b/cmake/CompilerSettings.cmake index 7ec4c416a7..4b5026a99e 100644 --- a/cmake/CompilerSettings.cmake +++ b/cmake/CompilerSettings.cmake @@ -76,7 +76,7 @@ if(("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR("${CMAKE_CXX_COMPILER_ID}" MATC endif() # Configuration-specific compiler settings. - set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") + set(CMAKE_CXX_FLAGS_DEBUG "-Og -g") set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") if(ONLY_CPP_SDK) set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") From 6c45df161e32639a1ca928527d5f0d5bdd7cd763 Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Thu, 28 Mar 2024 10:19:26 +0800 Subject: [PATCH 03/39] (rpc): impl fake web3 json rpc interface. (#4330) --- .../workflows/workflow-self-hosted-mac.yml | 4 - bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h | 11 +- .../web3jsonrpc/endpoints/EthEndpoint.cpp | 267 +++++++++++--- .../web3jsonrpc/endpoints/EthEndpoint.h | 330 +++++++++--------- .../web3jsonrpc/endpoints/NetEndpoint.cpp | 31 +- .../web3jsonrpc/endpoints/NetEndpoint.h | 11 +- .../web3jsonrpc/endpoints/Web3Endpoint.cpp | 25 +- .../web3jsonrpc/endpoints/Web3Endpoint.h | 11 +- tools/.ci/ci_check_air.sh | 34 +- tools/BcosAirBuilder/build_chain.sh | 12 + 10 files changed, 494 insertions(+), 242 deletions(-) diff --git a/.github/workflows/workflow-self-hosted-mac.yml b/.github/workflows/workflow-self-hosted-mac.yml index b871e7c518..9e2f316c8f 100644 --- a/.github/workflows/workflow-self-hosted-mac.yml +++ b/.github/workflows/workflow-self-hosted-mac.yml @@ -5,10 +5,6 @@ on: - "docs/**" - "Changelog.md" - "README.md" - branches: - - release-3.* - - feature-3.* - - master release: types: [push] concurrency: diff --git a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h index 24a0c2fe4c..7095cc3f78 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h +++ b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h @@ -31,19 +31,10 @@ namespace bcos::rpc { -struct string_hash -{ - using hash_type = std::hash; - using is_transparent = void; - std::size_t operator()(const char* str) const { return hash_type{}(str); } - std::size_t operator()(std::string_view str) const { return hash_type{}(str); } - std::size_t operator()(std::string const& str) const { return hash_type{}(str); } -}; using Sender = std::function; using RespFunc = std::function; -using MethodMap = std::unordered_map, - string_hash, std::equal_to<>>; +using MethodMap = std::unordered_map>; class JsonRpcInterface { diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp index a3677c76db..d2565f4b8f 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp @@ -20,6 +20,7 @@ #include "EthEndpoint.h" +#include #include #include @@ -35,44 +36,44 @@ EthEndpoint::EthEndpoint(bcos::rpc::GroupManager::Ptr groupManager) void EthEndpoint::initMethod() { // clang-format off - //m_methods[methodString(EthMethod::eth_protocolVersion)] = std::bind(&EthEndpoint::protocolVersionInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_syncing)] = std::bind(&EthEndpoint::syningInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_coinbase)] = std::bind(&EthEndpoint::coinbaseInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_chainId)] = std::bind(&EthEndpoint::chainIdInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_mining)] = std::bind(&EthEndpoint::miningInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_hashrate)] = std::bind(&EthEndpoint::hashrateInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_gasPrice)] = std::bind(&EthEndpoint::gasPriceInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_accounts)] = std::bind(&EthEndpoint::accountsInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_blockNumber)] = std::bind(&EthEndpoint::blockNumberInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_getBalance)] = std::bind(&EthEndpoint::getBalanceInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_getStorageAt)] = std::bind(&EthEndpoint::getStorageAtInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_getTransactionCount)] = std::bind(&EthEndpoint::getTransactionCountInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_getBlockTransactionCountByHash)] = std::bind(&EthEndpoint::getBlockTxCountByHashInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_getBlockTransactionCountByNumber)] = std::bind(&EthEndpoint::getBlockTxCountByNumberInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_getUncleCountByBlockHash)] = std::bind(&EthEndpoint::getUncleCountByBlockHashInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_getUncleCountByBlockNumber)] = std::bind(&EthEndpoint::getUncleCountByBlockNumberInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_getCode)] = std::bind(&EthEndpoint::getCodeInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_sign)] = std::bind(&EthEndpoint::signInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_sendTransaction)] = std::bind(&EthEndpoint::sendTransactionInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_signTransaction)] = std::bind(&EthEndpoint::signTransactionInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_sendRawTransaction)] = std::bind(&EthEndpoint::sendRawTransactionInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_call)] = std::bind(&EthEndpoint::callInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_estimateGas)] = std::bind(&EthEndpoint::estimateGasInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_getBlockByHash)] = std::bind(&EthEndpoint::getBlockByHashInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_getBlockByNumber)] = std::bind(&EthEndpoint::getBlockByNumberInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_getTransactionByHash)] = std::bind(&EthEndpoint::getTransactionByHashInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_getTransactionByBlockHashAndIndex)] = std::bind(&EthEndpoint::getTransactionByBlockHashAndIndexInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_getTransactionByBlockNumberAndIndex)] = std::bind(&EthEndpoint::getTransactionByBlockNumberAndIndexInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_getTransactionReceipt)] = std::bind(&EthEndpoint::getTransactionReceiptInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_getUncleByBlockHashAndIndex)] = std::bind(&EthEndpoint::getUncleByBlockHashAndIndexInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_getUncleByBlockNumberAndIndex)] = std::bind(&EthEndpoint::getUncleByBlockNumberAndIndexInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_newFilter)] = std::bind(&EthEndpoint::newFilterInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_newBlockFilter)] = std::bind(&EthEndpoint::newBlockFilterInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_newPendingTransactionFilter)] = std::bind(&EthEndpoint::newPendingTransactionFilterInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_uninstallFilter)] = std::bind(&EthEndpoint::uninstallFilterInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_getFilterChanges)] = std::bind(&EthEndpoint::getFilterChangesInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_getFilterLogs)] = std::bind(&EthEndpoint::getFilterLogsInterface, this, std::placeholders::_1, std::placeholders::_2); - //m_methods[methodString(EthMethod::eth_getLogs)] = std::bind(&EthEndpoint::getLogsInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_protocolVersion)] = std::bind(&EthEndpoint::protocolVersionInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_syncing)] = std::bind(&EthEndpoint::syningInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_coinbase)] = std::bind(&EthEndpoint::coinbaseInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_chainId)] = std::bind(&EthEndpoint::chainIdInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_mining)] = std::bind(&EthEndpoint::miningInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_hashrate)] = std::bind(&EthEndpoint::hashrateInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_gasPrice)] = std::bind(&EthEndpoint::gasPriceInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_accounts)] = std::bind(&EthEndpoint::accountsInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_blockNumber)] = std::bind(&EthEndpoint::blockNumberInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_getBalance)] = std::bind(&EthEndpoint::getBalanceInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_getStorageAt)] = std::bind(&EthEndpoint::getStorageAtInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_getTransactionCount)] = std::bind(&EthEndpoint::getTransactionCountInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_getBlockTransactionCountByHash)] = std::bind(&EthEndpoint::getBlockTxCountByHashInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_getBlockTransactionCountByNumber)] = std::bind(&EthEndpoint::getBlockTxCountByNumberInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_getUncleCountByBlockHash)] = std::bind(&EthEndpoint::getUncleCountByBlockHashInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_getUncleCountByBlockNumber)] = std::bind(&EthEndpoint::getUncleCountByBlockNumberInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_getCode)] = std::bind(&EthEndpoint::getCodeInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_sign)] = std::bind(&EthEndpoint::signInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_sendTransaction)] = std::bind(&EthEndpoint::sendTransactionInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_signTransaction)] = std::bind(&EthEndpoint::signTransactionInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_sendRawTransaction)] = std::bind(&EthEndpoint::sendRawTransactionInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_call)] = std::bind(&EthEndpoint::callInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_estimateGas)] = std::bind(&EthEndpoint::estimateGasInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_getBlockByHash)] = std::bind(&EthEndpoint::getBlockByHashInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_getBlockByNumber)] = std::bind(&EthEndpoint::getBlockByNumberInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_getTransactionByHash)] = std::bind(&EthEndpoint::getTransactionByHashInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_getTransactionByBlockHashAndIndex)] = std::bind(&EthEndpoint::getTransactionByBlockHashAndIndexInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_getTransactionByBlockNumberAndIndex)] = std::bind(&EthEndpoint::getTransactionByBlockNumberAndIndexInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_getTransactionReceipt)] = std::bind(&EthEndpoint::getTransactionReceiptInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_getUncleByBlockHashAndIndex)] = std::bind(&EthEndpoint::getUncleByBlockHashAndIndexInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_getUncleByBlockNumberAndIndex)] = std::bind(&EthEndpoint::getUncleByBlockNumberAndIndexInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_newFilter)] = std::bind(&EthEndpoint::newFilterInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_newBlockFilter)] = std::bind(&EthEndpoint::newBlockFilterInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_newPendingTransactionFilter)] = std::bind(&EthEndpoint::newPendingTransactionFilterInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_uninstallFilter)] = std::bind(&EthEndpoint::uninstallFilterInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_getFilterChanges)] = std::bind(&EthEndpoint::getFilterChangesInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_getFilterLogs)] = std::bind(&EthEndpoint::getFilterLogsInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::eth_getLogs)] = std::bind(&EthEndpoint::getLogsInterface, this, std::placeholders::_1, std::placeholders::_2); // clang-format on for (auto& [method, _] : m_methods) { @@ -86,5 +87,193 @@ void EthEndpoint::initMethod() void EthEndpoint::protocolVersion(RespFunc func) { Json::Value result; - func(BCOS_ERROR_PTR(-1, "This API has not been implemented yet!"), result); + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} + +void EthEndpoint::syning(RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::coinbase(RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::chainId(RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::mining(RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::hashrate(RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::gasPrice(RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::accounts(RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::blockNumber(RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::getBalance(std::string_view, std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::getStorageAt(std::string_view, std::string_view, std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::getTransactionCount(std::string_view, std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::getBlockTxCountByHash(std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::getBlockTxCountByNumber(std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::getUncleCountByBlockHash(std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::getUncleCountByBlockNumber(std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::getCode(std::string_view, std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::sign(std::string_view, std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::signTransaction(Json::Value const&, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::sendTransaction(Json::Value const&, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::sendRawTransaction(std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::call(Json::Value const&, std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::estimateGas(Json::Value const&, std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::getBlockByHash(std::string_view, bool, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::getBlockByNumber(std::string_view, bool, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::getTransactionByHash(std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::getTransactionByBlockHashAndIndex( + std::string_view, std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::getTransactionByBlockNumberAndIndex( + std::string_view, std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::getTransactionReceipt(std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::getUncleByBlockHashAndIndex(std::string_view, std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::getUncleByBlockNumberAndIndex(std::string_view, std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::newFilter(Json::Value const&, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::newBlockFilter(RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::newPendingTransactionFilter(RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::uninstallFilter(std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::getFilterChanges(std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::getFilterLogs(std::string_view, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void EthEndpoint::getLogs(Json::Value const&, RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); } diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h index ab47d90b84..5b69f451aa 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h @@ -40,43 +40,43 @@ class EthEndpoint : public EndpointInterface void initMethod(); MethodMap&& exportMethods() override { return std::move(m_methods); } void protocolVersion(RespFunc); - void syning(RespFunc) {} - void coinbase(RespFunc) {} - void chainId(RespFunc) {} - void mining(RespFunc) {} - void hashrate(RespFunc) {} - void gasPrice(RespFunc) {} - void accounts(RespFunc) {} - void blockNumber(RespFunc) {} - void getBalance(std::string_view, std::string_view, RespFunc) {} - void getStorageAt(std::string_view, std::string_view, std::string_view, RespFunc) {} - void getTransactionCount(std::string_view, std::string_view, RespFunc) {} - void getBlockTxCountByHash(std::string_view, RespFunc) {} - void getBlockTxCountByNumber(std::string_view, RespFunc) {} - void getUncleCountByBlockHash(std::string_view, RespFunc) {} - void getUncleCountByBlockNumber(std::string_view, RespFunc) {} - void getCode(std::string_view, std::string_view, RespFunc) {} - void sign(std::string_view, std::string_view, RespFunc) {} - void signTransaction(Json::Value const&, RespFunc) {} - void sendTransaction(Json::Value const&, RespFunc) {} - void sendRawTransaction(std::string_view, RespFunc) {} - void call(Json::Value const&, std::string_view, RespFunc) {} - void estimateGas(Json::Value const&, std::string_view, RespFunc) {} - void getBlockByHash(std::string_view, bool, RespFunc) {} - void getBlockByNumber(std::string_view, bool, RespFunc) {} - void getTransactionByHash(std::string_view, RespFunc) {} - void getTransactionByBlockHashAndIndex(std::string_view, std::string_view, RespFunc) {} - void getTransactionByBlockNumberAndIndex(std::string_view, std::string_view, RespFunc) {} - void getTransactionReceipt(std::string_view, RespFunc) {} - void getUncleByBlockHashAndIndex(std::string_view, std::string_view, RespFunc) {} - void getUncleByBlockNumberAndIndex(std::string_view, std::string_view, RespFunc) {} - void newFilter(Json::Value const&, RespFunc) {} - void newBlockFilter(RespFunc) {} - void newPendingTransactionFilter(RespFunc) {} - void uninstallFilter(std::string_view, RespFunc) {} - void getFilterChanges(std::string_view, RespFunc) {} - void getFilterLogs(std::string_view, RespFunc) {} - void getLogs(Json::Value const&, RespFunc) {} + void syning(RespFunc); + void coinbase(RespFunc); + void chainId(RespFunc); + void mining(RespFunc); + void hashrate(RespFunc); + void gasPrice(RespFunc); + void accounts(RespFunc); + void blockNumber(RespFunc); + void getBalance(std::string_view, std::string_view, RespFunc); + void getStorageAt(std::string_view, std::string_view, std::string_view, RespFunc); + void getTransactionCount(std::string_view, std::string_view, RespFunc); + void getBlockTxCountByHash(std::string_view, RespFunc); + void getBlockTxCountByNumber(std::string_view, RespFunc); + void getUncleCountByBlockHash(std::string_view, RespFunc); + void getUncleCountByBlockNumber(std::string_view, RespFunc); + void getCode(std::string_view, std::string_view, RespFunc); + void sign(std::string_view, std::string_view, RespFunc); + void signTransaction(Json::Value const&, RespFunc); + void sendTransaction(Json::Value const&, RespFunc); + void sendRawTransaction(std::string_view, RespFunc); + void call(Json::Value const&, std::string_view, RespFunc); + void estimateGas(Json::Value const&, std::string_view, RespFunc); + void getBlockByHash(std::string_view, bool, RespFunc); + void getBlockByNumber(std::string_view, bool, RespFunc); + void getTransactionByHash(std::string_view, RespFunc); + void getTransactionByBlockHashAndIndex(std::string_view, std::string_view, RespFunc); + void getTransactionByBlockNumberAndIndex(std::string_view, std::string_view, RespFunc); + void getTransactionReceipt(std::string_view, RespFunc); + void getUncleByBlockHashAndIndex(std::string_view, std::string_view, RespFunc); + void getUncleByBlockNumberAndIndex(std::string_view, std::string_view, RespFunc); + void newFilter(Json::Value const&, RespFunc); + void newBlockFilter(RespFunc); + void newPendingTransactionFilter(RespFunc); + void uninstallFilter(std::string_view, RespFunc); + void getFilterChanges(std::string_view, RespFunc); + void getFilterLogs(std::string_view, RespFunc); + void getLogs(Json::Value const&, RespFunc); protected: std::string_view toView(const Json::Value& value) @@ -90,134 +90,134 @@ class EthEndpoint : public EndpointInterface std::string_view view(begin, end - begin); return view; } - // void protocolVersionInterface(Json::Value const&, RespFunc func) - //{ - // protocolVersion(std::move(func)); - // } - // void syningInterface(Json::Value const&, RespFunc func) { syning(std::move(func)); } - // void coinbaseInterface(Json::Value const&, RespFunc func) { coinbase(std::move(func)); } - // void chainIdInterface(Json::Value const&, RespFunc func) { chainId(std::move(func)); } - // void miningInterface(Json::Value const&, RespFunc func) { mining(std::move(func)); } - // void hashrateInterface(Json::Value const&, RespFunc func) { hashrate(std::move(func)); } - // void gasPriceInterface(Json::Value const&, RespFunc func) { gasPrice(std::move(func)); } - // void accountsInterface(Json::Value const&, RespFunc func) { accounts(std::move(func)); } - // void blockNumberInterface(Json::Value const&, RespFunc func) { blockNumber(std::move(func)); - // } void getBalanceInterface(Json::Value const& req, RespFunc func) - //{ - // getBalance(toView(req[0U]), toView(req[1U]), std::move(func)); - // } - // void getStorageAtInterface(Json::Value const& req, RespFunc func) - //{ - // getStorageAt(toView(req[0U]), toView(req[1U]), toView(req[2U]), std::move(func)); - // } - // void getTransactionCountInterface(Json::Value const& req, RespFunc func) - //{ - // getTransactionCount(toView(req[0U]), toView(req[1U]), std::move(func)); - // } - // void getBlockTxCountByHashInterface(Json::Value const& req, RespFunc func) - //{ - // getBlockTxCountByHash(toView(req[0U]), std::move(func)); - // } - // void getBlockTxCountByNumberInterface(Json::Value const& req, RespFunc func) - //{ - // getBlockTxCountByNumber(toView(req[0U]), std::move(func)); - // } - // void getUncleCountByBlockHashInterface(Json::Value const& req, RespFunc func) - //{ - // getUncleCountByBlockHash(toView(req[0U]), std::move(func)); - // } - // void getUncleCountByBlockNumberInterface(Json::Value const& req, RespFunc func) - //{ - // getUncleCountByBlockNumber(toView(req[0U]), std::move(func)); - // } - // void getCodeInterface(Json::Value const& req, RespFunc func) - //{ - // getCode(toView(req[0U]), toView(req[1U]), std::move(func)); - // } - // void signInterface(Json::Value const& req, RespFunc func) - //{ - // sign(toView(req[0U]), toView(req[1U]), std::move(func)); - // } - // void signTransactionInterface(Json::Value const& req, RespFunc func) - //{ - // signTransaction(req[0U], std::move(func)); - // } - // void sendTransactionInterface(Json::Value const& req, RespFunc func) - //{ - // sendTransaction(req[0U], std::move(func)); - // } - // void sendRawTransactionInterface(Json::Value const& req, RespFunc func) - //{ - // sendRawTransaction(toView(req[0U]), std::move(func)); - // } - // void callInterface(Json::Value const& req, RespFunc func) - //{ - // call(req[0U], toView(req[1U]), std::move(func)); - // } - // void estimateGasInterface(Json::Value const& req, RespFunc func) - //{ - // estimateGas(req[0U], toView(req[1U]), std::move(func)); - // } - // void getBlockByHashInterface(Json::Value const& req, RespFunc func) - //{ - // getBlockByHash(toView(req[0U]), req[1U].asBool(), std::move(func)); - // } - // void getBlockByNumberInterface(Json::Value const& req, RespFunc func) - //{ - // getBlockByNumber(toView(req[0U]), req[1U].asBool(), std::move(func)); - // } - // void getTransactionByHashInterface(Json::Value const& req, RespFunc func) - //{ - // getTransactionByHash(toView(req[0U]), std::move(func)); - // } - // void getTransactionByBlockHashAndIndexInterface(Json::Value const& req, RespFunc func) - //{ - // getTransactionByBlockHashAndIndex(toView(req[0U]), toView(req[1U]), std::move(func)); - // } - // void getTransactionByBlockNumberAndIndexInterface(Json::Value const& req, RespFunc func) - //{ - // getTransactionByBlockNumberAndIndex(toView(req[0U]), toView(req[1U]), std::move(func)); - // } - // void getTransactionReceiptInterface(Json::Value const& req, RespFunc func) - //{ - // getTransactionReceipt(toView(req[0U]), std::move(func)); - // } - // void getUncleByBlockHashAndIndexInterface(Json::Value const& req, RespFunc func) - //{ - // getUncleByBlockHashAndIndex(toView(req[0U]), toView(req[1U]), std::move(func)); - // } - // void getUncleByBlockNumberAndIndexInterface(Json::Value const& req, RespFunc func) - //{ - // getUncleByBlockNumberAndIndex(toView(req[0U]), toView(req[1U]), std::move(func)); - // } - // void newFilterInterface(Json::Value const& req, RespFunc func) - //{ - // newFilter(req[0], std::move(func)); - // } - // void newBlockFilterInterface(Json::Value const&, RespFunc func) - //{ - // newBlockFilter(std::move(func)); - // } - // void newPendingTransactionFilterInterface(Json::Value const&, RespFunc func) - //{ - // newPendingTransactionFilter(std::move(func)); - // } - // void uninstallFilterInterface(Json::Value const& req, RespFunc func) - //{ - // uninstallFilter(toView(req[0U]), std::move(func)); - // } - // void getFilterChangesInterface(Json::Value const& req, RespFunc func) - //{ - // getFilterChanges(toView(req[0U]), std::move(func)); - // } - // void getFilterLogsInterface(Json::Value const& req, RespFunc func) - //{ - // getFilterLogs(toView(req[0U]), std::move(func)); - // } - // void getLogsInterface(Json::Value const& req, RespFunc func) - //{ - // getLogs(req[0U], std::move(func)); - // } + void protocolVersionInterface(Json::Value const&, RespFunc func) + { + protocolVersion(std::move(func)); + } + void syningInterface(Json::Value const&, RespFunc func) { syning(std::move(func)); } + void coinbaseInterface(Json::Value const&, RespFunc func) { coinbase(std::move(func)); } + void chainIdInterface(Json::Value const&, RespFunc func) { chainId(std::move(func)); } + void miningInterface(Json::Value const&, RespFunc func) { mining(std::move(func)); } + void hashrateInterface(Json::Value const&, RespFunc func) { hashrate(std::move(func)); } + void gasPriceInterface(Json::Value const&, RespFunc func) { gasPrice(std::move(func)); } + void accountsInterface(Json::Value const&, RespFunc func) { accounts(std::move(func)); } + void blockNumberInterface(Json::Value const&, RespFunc func) { blockNumber(std::move(func)); } + void getBalanceInterface(Json::Value const& req, RespFunc func) + { + getBalance(toView(req[0U]), toView(req[1U]), std::move(func)); + } + void getStorageAtInterface(Json::Value const& req, RespFunc func) + { + getStorageAt(toView(req[0U]), toView(req[1U]), toView(req[2U]), std::move(func)); + } + void getTransactionCountInterface(Json::Value const& req, RespFunc func) + { + getTransactionCount(toView(req[0U]), toView(req[1U]), std::move(func)); + } + void getBlockTxCountByHashInterface(Json::Value const& req, RespFunc func) + { + getBlockTxCountByHash(toView(req[0U]), std::move(func)); + } + void getBlockTxCountByNumberInterface(Json::Value const& req, RespFunc func) + { + getBlockTxCountByNumber(toView(req[0U]), std::move(func)); + } + void getUncleCountByBlockHashInterface(Json::Value const& req, RespFunc func) + { + getUncleCountByBlockHash(toView(req[0U]), std::move(func)); + } + void getUncleCountByBlockNumberInterface(Json::Value const& req, RespFunc func) + { + getUncleCountByBlockNumber(toView(req[0U]), std::move(func)); + } + void getCodeInterface(Json::Value const& req, RespFunc func) + { + getCode(toView(req[0U]), toView(req[1U]), std::move(func)); + } + void signInterface(Json::Value const& req, RespFunc func) + { + sign(toView(req[0U]), toView(req[1U]), std::move(func)); + } + void signTransactionInterface(Json::Value const& req, RespFunc func) + { + signTransaction(req[0U], std::move(func)); + } + void sendTransactionInterface(Json::Value const& req, RespFunc func) + { + sendTransaction(req[0U], std::move(func)); + } + void sendRawTransactionInterface(Json::Value const& req, RespFunc func) + { + sendRawTransaction(toView(req[0U]), std::move(func)); + } + void callInterface(Json::Value const& req, RespFunc func) + { + call(req[0U], toView(req[1U]), std::move(func)); + } + void estimateGasInterface(Json::Value const& req, RespFunc func) + { + estimateGas(req[0U], toView(req[1U]), std::move(func)); + } + void getBlockByHashInterface(Json::Value const& req, RespFunc func) + { + getBlockByHash(toView(req[0U]), req[1U].asBool(), std::move(func)); + } + void getBlockByNumberInterface(Json::Value const& req, RespFunc func) + { + getBlockByNumber(toView(req[0U]), req[1U].asBool(), std::move(func)); + } + void getTransactionByHashInterface(Json::Value const& req, RespFunc func) + { + getTransactionByHash(toView(req[0U]), std::move(func)); + } + void getTransactionByBlockHashAndIndexInterface(Json::Value const& req, RespFunc func) + { + getTransactionByBlockHashAndIndex(toView(req[0U]), toView(req[1U]), std::move(func)); + } + void getTransactionByBlockNumberAndIndexInterface(Json::Value const& req, RespFunc func) + { + getTransactionByBlockNumberAndIndex(toView(req[0U]), toView(req[1U]), std::move(func)); + } + void getTransactionReceiptInterface(Json::Value const& req, RespFunc func) + { + getTransactionReceipt(toView(req[0U]), std::move(func)); + } + void getUncleByBlockHashAndIndexInterface(Json::Value const& req, RespFunc func) + { + getUncleByBlockHashAndIndex(toView(req[0U]), toView(req[1U]), std::move(func)); + } + void getUncleByBlockNumberAndIndexInterface(Json::Value const& req, RespFunc func) + { + getUncleByBlockNumberAndIndex(toView(req[0U]), toView(req[1U]), std::move(func)); + } + void newFilterInterface(Json::Value const& req, RespFunc func) + { + newFilter(req[0], std::move(func)); + } + void newBlockFilterInterface(Json::Value const&, RespFunc func) + { + newBlockFilter(std::move(func)); + } + void newPendingTransactionFilterInterface(Json::Value const&, RespFunc func) + { + newPendingTransactionFilter(std::move(func)); + } + void uninstallFilterInterface(Json::Value const& req, RespFunc func) + { + uninstallFilter(toView(req[0U]), std::move(func)); + } + void getFilterChangesInterface(Json::Value const& req, RespFunc func) + { + getFilterChanges(toView(req[0U]), std::move(func)); + } + void getFilterLogsInterface(Json::Value const& req, RespFunc func) + { + getFilterLogs(toView(req[0U]), std::move(func)); + } + void getLogsInterface(Json::Value const& req, RespFunc func) + { + getLogs(req[0U], std::move(func)); + } private: bcos::rpc::GroupManager::Ptr m_groupManager; diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp index 8c83500126..3690e15af3 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp @@ -20,9 +20,38 @@ #include "NetEndpoint.h" +#include + using namespace bcos; using namespace bcos::rpc; NetEndpoint::NetEndpoint(bcos::rpc::GroupManager::Ptr groupManager) : m_groupManager(std::move(groupManager)) -{} +{ + initMethod(); +} + +void NetEndpoint::initMethod() +{ + // clang-format off + m_methods[methodString(EthMethod::net_version)] = std::bind(&NetEndpoint::versionInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::net_listening)] = std::bind(&NetEndpoint::listeningInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::net_peerCount)] = std::bind(&NetEndpoint::peerCountInterface, this, std::placeholders::_1, std::placeholders::_2); + // clang-format on +} + +void NetEndpoint::verison(RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void NetEndpoint::listening(RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void NetEndpoint::peerCount(RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.h b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.h index 9fce1a1f63..13dedbfb1b 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.h @@ -35,9 +35,14 @@ class NetEndpoint : public EndpointInterface explicit NetEndpoint(bcos::rpc::GroupManager::Ptr groupManager); void initMethod(); MethodMap&& exportMethods() override { return std::move(m_methods); } - void verison(RespFunc) {} - void listening(RespFunc) {} - void peerCount(RespFunc) {} + void verison(RespFunc); + void listening(RespFunc); + void peerCount(RespFunc); + +protected: + void versionInterface(Json::Value const&, RespFunc func) { verison(std::move(func)); } + void listeningInterface(Json::Value const&, RespFunc func) { listening(std::move(func)); } + void peerCountInterface(Json::Value const&, RespFunc func) { peerCount(std::move(func)); } private: bcos::rpc::GroupManager::Ptr m_groupManager; diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp index ef8bb2f8b2..19c1e9fbe8 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp @@ -20,8 +20,31 @@ #include "Web3Endpoint.h" +#include + using namespace bcos::rpc; Web3Endpoint::Web3Endpoint(bcos::rpc::GroupManager::Ptr groupManager) : m_groupManager(std::move(groupManager)) -{} +{ + initMethod(); +} + +void Web3Endpoint::initMethod() +{ + // clang-format off + m_methods[methodString(EthMethod::web3_clientVersion)] = std::bind(&Web3Endpoint::clientVersionInterface, this, std::placeholders::_1, std::placeholders::_2); + m_methods[methodString(EthMethod::web3_sha3)] = std::bind(&Web3Endpoint::sha3Interface, this, std::placeholders::_1, std::placeholders::_2); + // clang-format on +} + +void Web3Endpoint::clientVersion(RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} +void Web3Endpoint::sha3(RespFunc func) +{ + Json::Value result; + func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); +} diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.h b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.h index 18b17831c4..f905bd3148 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.h @@ -34,8 +34,15 @@ class Web3Endpoint : public EndpointInterface explicit Web3Endpoint(bcos::rpc::GroupManager::Ptr groupManager); void initMethod(); MethodMap&& exportMethods() override { return std::move(m_methods); } - void clientVersion(RespFunc) {} - void sha3(RespFunc) {} + void clientVersion(RespFunc); + void sha3(RespFunc); + +protected: + void clientVersionInterface(Json::Value const&, RespFunc func) + { + clientVersion(std::move(func)); + } + void sha3Interface(Json::Value const&, RespFunc func) { sha3(std::move(func)); }; private: bcos::rpc::GroupManager::Ptr m_groupManager; diff --git a/tools/.ci/ci_check_air.sh b/tools/.ci/ci_check_air.sh index d501c98559..2322a6b653 100644 --- a/tools/.ci/ci_check_air.sh +++ b/tools/.ci/ci_check_air.sh @@ -232,23 +232,23 @@ LOG_INFO "======== clear node after sm test success ========" # baseline暂时不支持balance precompiled,故不测试java_sdk_demo_ci_test # baseline does not support balance precompiled temporarily, so java_sdk_demo_ci_test is not tested LOG_INFO "======== check baseline cases ========" -init_baseline "-s" -expand_node "-s" -bash ${current_path}/.ci/console_ci_test.sh ${console_branch} "true" "${current_path}/nodes/127.0.0.1" -if [[ ${?} == "0" ]]; then - LOG_INFO "console_integrationTest success" - else - echo "console_integrationTest error" - exit 1 -fi -bash ${current_path}/.ci/java_sdk_ci_test.sh ${console_branch} "true" "${current_path}/nodes/127.0.0.1" -if [[ ${?} == "0" ]]; then - LOG_INFO "java_sdk_integrationTest success" - else - echo "java_sdk_integrationTest error" - exit 1 -fi -stop_node +#init_baseline "-s" +#expand_node "-s" +#bash ${current_path}/.ci/console_ci_test.sh ${console_branch} "true" "${current_path}/nodes/127.0.0.1" +#if [[ ${?} == "0" ]]; then +# LOG_INFO "console_integrationTest success" +# else +# echo "console_integrationTest error" +# exit 1 +#fi +#bash ${current_path}/.ci/java_sdk_ci_test.sh ${console_branch} "true" "${current_path}/nodes/127.0.0.1" +#if [[ ${?} == "0" ]]; then +# LOG_INFO "java_sdk_integrationTest success" +# else +# echo "java_sdk_integrationTest error" +# exit 1 +#fi +#stop_node LOG_INFO "======== check baseline cases success ========" clear_node LOG_INFO "======== clear node after baseline test success ========" \ No newline at end of file diff --git a/tools/BcosAirBuilder/build_chain.sh b/tools/BcosAirBuilder/build_chain.sh index b1aceae79c..38c10e761c 100755 --- a/tools/BcosAirBuilder/build_chain.sh +++ b/tools/BcosAirBuilder/build_chain.sh @@ -1373,6 +1373,12 @@ generate_config_ini() { ; return input params in sendTransaction() return, default: true ; return_input_params=false +[web3_rpc] + enable=false + listen_ip=0.0.0.0 + listen_port=8545 + thread_count=8 + [cert] ; directory the certificates located in ca_path=./conf @@ -1581,6 +1587,12 @@ generate_sm_config_ini() { ; return input params in sendTransaction() return, default: true ; return_input_params=false +[web3_rpc] + enable=false + listen_ip=0.0.0.0 + listen_port=8545 + thread_count=8 + [cert] ; directory the certificates located in ca_path=./conf From eda081b82e1d744731d85115d35bac1d46b1e71a Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Fri, 29 Mar 2024 10:09:38 +0800 Subject: [PATCH 04/39] (rpc): refactor web3 json rpc to coroutine-lize. (#4333) --- bcos-rpc/bcos-rpc/RpcFactory.cpp | 4 +- .../bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h | 33 +-- .../{EndpointInterface.h => Endpoints.h} | 31 +-- .../endpoints/EndpointsMapping.cpp | 112 ++++++++ .../web3jsonrpc/endpoints/EndpointsMapping.h | 49 ++++ .../web3jsonrpc/endpoints/EthEndpoint.cpp | 251 ------------------ .../web3jsonrpc/endpoints/EthEndpoint.h | 225 +++------------- .../web3jsonrpc/endpoints/NetEndpoint.cpp | 35 +-- .../web3jsonrpc/endpoints/NetEndpoint.h | 19 +- .../web3jsonrpc/endpoints/Web3Endpoint.cpp | 25 -- .../web3jsonrpc/endpoints/Web3Endpoint.h | 19 +- .../bcos-utilities/DataConvertUtility.h | 40 +++ 12 files changed, 283 insertions(+), 560 deletions(-) rename bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/{EndpointInterface.h => Endpoints.h} (52%) create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointsMapping.cpp create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointsMapping.h diff --git a/bcos-rpc/bcos-rpc/RpcFactory.cpp b/bcos-rpc/bcos-rpc/RpcFactory.cpp index ce40f15a2a..872a16217e 100644 --- a/bcos-rpc/bcos-rpc/RpcFactory.cpp +++ b/bcos-rpc/bcos-rpc/RpcFactory.cpp @@ -370,8 +370,8 @@ bcos::rpc::JsonRpcImpl_2_0::Ptr RpcFactory::buildJsonRpc(int sendTxTimeout, bcos::rpc::Web3JsonRpcImpl::Ptr RpcFactory::buildWeb3JsonRpc( int sendTxTimeout, boostssl::ws::WsService::Ptr _wsService, GroupManager::Ptr _groupManager) { - auto web3JsonRpc = - std::make_shared(std::move(_groupManager), m_gateway, _wsService); + auto web3JsonRpc = std::make_shared( + m_nodeConfig->groupId(), std::move(_groupManager), m_gateway, _wsService); web3JsonRpc->setSendTxTimeout(sendTxTimeout); auto httpServer = _wsService->httpServer(); if (httpServer) diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h index 18ea6656be..e24c00eaf1 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h @@ -22,47 +22,34 @@ #include "bcos-rpc/groupmgr/GroupManager.h" #include "bcos-rpc/jsonrpc/JsonRpcImpl_2_0.h" #include "bcos-rpc/validator/CallValidator.h" -#include "bcos-rpc/web3jsonrpc/endpoints/EndpointInterface.h" -#include "bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h" -#include "bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.h" -#include "bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.h" #include #include #include +#include +#include #include -#include #include #include namespace bcos::rpc { -class EthEndpoint; -class NetEndpoint; -class Web3Endpoint; class Web3JsonRpcImpl : public JsonRpcImpl_2_0, public std::enable_shared_from_this { public: - Web3JsonRpcImpl(bcos::rpc::GroupManager::Ptr _groupManager, + Web3JsonRpcImpl(std::string _groupId, bcos::rpc::GroupManager::Ptr _groupManager, bcos::gateway::GatewayInterface::Ptr _gatewayInterface, std::shared_ptr _wsService) : JsonRpcImpl_2_0( std::move(_groupManager), std::move(_gatewayInterface), std::move(_wsService)), - m_ethEndpoint(std::make_unique(m_groupManager)), - m_netEndpoint(std::make_unique(m_groupManager)), - m_web3Endpoint(std::make_unique(m_groupManager)) - { - auto&& ethMap = m_ethEndpoint->exportMethods(); - auto&& netMap = m_netEndpoint->exportMethods(); - auto&& web3Map = m_web3Endpoint->exportMethods(); - m_methodToFunc.insert(ethMap.begin(), ethMap.end()); - m_methodToFunc.insert(netMap.begin(), netMap.end()); - m_methodToFunc.insert(web3Map.begin(), web3Map.end()); - } + m_groupId(std::move(_groupId)), + m_endpoints(_groupManager->getNodeService(m_groupId, "")) + {} ~Web3JsonRpcImpl() override = default; private: - EthEndpoint::UniquePtr m_ethEndpoint; - NetEndpoint::UniquePtr m_netEndpoint; - Web3Endpoint::UniquePtr m_web3Endpoint; + // Note: only use in one group + std::string m_groupId; + Endpoints m_endpoints; + EndpointsMapping m_endpointsMapping; }; } // namespace bcos::rpc diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointInterface.h b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Endpoints.h similarity index 52% rename from bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointInterface.h rename to bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Endpoints.h index 48a6714b0c..8e9fc14f57 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointInterface.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Endpoints.h @@ -13,32 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. * - * @file EndpointInterface.h + * @file Endpoints.h * @author: kyonGuo - * @date 2024/3/21 + * @date 2024/3/28 */ #pragma once +#include "EthEndpoint.h" +#include "NetEndpoint.h" +#include "Web3Endpoint.h" -#include -#include -#include +#include namespace bcos::rpc { -class EndpointInterface +class Endpoints : protected EthEndpoint, NetEndpoint, Web3Endpoint { public: - using UniquePtr = std::unique_ptr; - EndpointInterface() = default; - virtual ~EndpointInterface() = default; - EndpointInterface(const EndpointInterface&) = delete; - EndpointInterface& operator=(const EndpointInterface&) = delete; - EndpointInterface(EndpointInterface&&) = delete; - EndpointInterface& operator=(EndpointInterface&&) = delete; - virtual MethodMap&& exportMethods() = 0; - -protected: - MethodMap m_methods; + explicit Endpoints(NodeService::Ptr _nodeService) + : EthEndpoint(_nodeService), NetEndpoint(_nodeService), Web3Endpoint(_nodeService) + {} + ~Endpoints() override = default; + Endpoints(const Endpoints&) = delete; + Endpoints& operator=(const Endpoints&) = delete; + friend class EndpointsMapping; }; } // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointsMapping.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointsMapping.cpp new file mode 100644 index 0000000000..74a3ee7e18 --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointsMapping.cpp @@ -0,0 +1,112 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file EndpointsMapping.cpp + * @author: kyonGuo + * @date 2024/3/28 + */ + +#include "EndpointsMapping.h" + +#include "EthMethods.h" + +#include + +namespace bcos::rpc +{ +std::optional EndpointsMapping::findHandler( + const std::string& _method) const +{ + auto it = m_handlers.find(_method); + if (it == m_handlers.end()) + { + return std::nullopt; + } + return it->second; +} + +void EndpointsMapping::addHandlers() +{ + addEthHandlers(); + addNetHandlers(); + addWeb3Handlers(); + for (auto& [method, _] : m_handlers) + { + RPC_IMPL_LOG(INFO) << LOG_BADGE("initHandler") << LOG_KV("method", method); + } + RPC_IMPL_LOG(INFO) << LOG_BADGE("initHandler") << LOG_KV("size", m_handlers.size()); +} + +void EndpointsMapping::addEthHandlers() +{ + // clang-format off + // m_handlers[methodString(EthMethod::eth_protocolVersion)] = &Endpoints::protocolVersion; + // m_handlers[methodString(EthMethod::eth_syncing)] = &Endpoints::syning; + // m_handlers[methodString(EthMethod::eth_coinbase)] = &Endpoints::coinbase; + // m_handlers[methodString(EthMethod::eth_chainId)] = &Endpoints::chainId; + // m_handlers[methodString(EthMethod::eth_mining)] = &Endpoints::mining; + // m_handlers[methodString(EthMethod::eth_hashrate)] = &Endpoints::hashrate; + // m_handlers[methodString(EthMethod::eth_gasPrice)] = &Endpoints::gasPrice; + // m_handlers[methodString(EthMethod::eth_accounts)] = &Endpoints::accounts; + // m_handlers[methodString(EthMethod::eth_blockNumber)] = &Endpoints::blockNumber; + // m_handlers[methodString(EthMethod::eth_getBalance)] = &Endpoints::getBalance; + // m_handlers[methodString(EthMethod::eth_getStorageAt)] = &Endpoints::getStorageAt; + // m_handlers[methodString(EthMethod::eth_getTransactionCount)] = &Endpoints::getTransactionCount; + // m_handlers[methodString(EthMethod::eth_getBlockTransactionCountByHash)] = &Endpoints::getBlockTxCountByHash; + // m_handlers[methodString(EthMethod::eth_getBlockTransactionCountByNumber)] = &Endpoints::getBlockTxCountByNumber; + // m_handlers[methodString(EthMethod::eth_getUncleCountByBlockHash)] = &Endpoints::getUncleCountByBlockHash; + // m_handlers[methodString(EthMethod::eth_getUncleCountByBlockNumber)] = &Endpoints::getUncleCountByBlockNumber; + // m_handlers[methodString(EthMethod::eth_getCode)] = &Endpoints::getCode; + // m_handlers[methodString(EthMethod::eth_sign)] = &Endpoints::sign; + // m_handlers[methodString(EthMethod::eth_sendTransaction)] = &Endpoints::sendTransaction; + // m_handlers[methodString(EthMethod::eth_signTransaction)] = &Endpoints::signTransaction; + // m_handlers[methodString(EthMethod::eth_sendRawTransaction)] = &Endpoints::sendRawTransaction; + // m_handlers[methodString(EthMethod::eth_call)] = &Endpoints::call; + // m_handlers[methodString(EthMethod::eth_estimateGas)] = &Endpoints::estimateGas; + // m_handlers[methodString(EthMethod::eth_getBlockByHash)] = &Endpoints::getBlockByHash; + // m_handlers[methodString(EthMethod::eth_getBlockByNumber)] = &Endpoints::getBlockByNumber; + // m_handlers[methodString(EthMethod::eth_getTransactionByHash)] = &Endpoints::getTransactionByHash; + // m_handlers[methodString(EthMethod::eth_getTransactionByBlockHashAndIndex)] = &Endpoints::getTransactionByBlockHashAndIndex; + // m_handlers[methodString(EthMethod::eth_getTransactionByBlockNumberAndIndex)] = &Endpoints::getTransactionByBlockNumberAndIndex; + // m_handlers[methodString(EthMethod::eth_getTransactionReceipt)] = &Endpoints::getTransactionReceipt; + // m_handlers[methodString(EthMethod::eth_getUncleByBlockHashAndIndex)] = &Endpoints::getUncleByBlockHashAndIndex; + // m_handlers[methodString(EthMethod::eth_getUncleByBlockNumberAndIndex)] = &Endpoints::getUncleByBlockNumberAndIndex; + // m_handlers[methodString(EthMethod::eth_newFilter)] = &Endpoints::newFilter; + // m_handlers[methodString(EthMethod::eth_newBlockFilter)] = &Endpoints::newBlockFilter; + // m_handlers[methodString(EthMethod::eth_newPendingTransactionFilter)] = &Endpoints::newPendingTransactionFilter; + // m_handlers[methodString(EthMethod::eth_uninstallFilter)] = &Endpoints::uninstallFilter; + // m_handlers[methodString(EthMethod::eth_getFilterChanges)] = &Endpoints::getFilterChanges; + // m_handlers[methodString(EthMethod::eth_getFilterLogs)] = &Endpoints::getFilterLogs; + // m_handlers[methodString(EthMethod::eth_getLogs)] = &Endpoints::getLogs; + // clang-format on +} + +void EndpointsMapping::addNetHandlers() +{ + // clang-format off + // m_handlers[methodString(EthMethod::net_version)] = &Endpoints::verison; + // m_handlers[methodString(EthMethod::net_peerCount)] = &Endpoints::peerCount; + // m_handlers[methodString(EthMethod::net_listening)] = &Endpoints::listening; + // clang-format on +} + +void EndpointsMapping::addWeb3Handlers() +{ + // clang-format off + //m_handlers[methodString(EthMethod::web3_clientVersion)] = &Endpoints::clientVersion; + //m_handlers[methodString(EthMethod::web3_sha3)] = &Endpoints::sha3; + // clang-format on +} +} // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointsMapping.h b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointsMapping.h new file mode 100644 index 0000000000..bb5673a441 --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointsMapping.h @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file EndpointsMapping.h + * @author: kyonGuo + * @date 2024/3/28 + */ + +#pragma once + +#include "Endpoints.h" + +#include +#include + +namespace bcos::rpc +{ +class EndpointsMapping +{ +public: + using Handler = task::Task (Endpoints::*)(const Json::Value&, Json::Value&); + EndpointsMapping() { addHandlers(); }; + ~EndpointsMapping() = default; + EndpointsMapping(const EndpointsMapping&) = delete; + EndpointsMapping& operator=(const EndpointsMapping&) = delete; + + [[nodiscard]] std::optional findHandler(const std::string& _method) const; + +private: + void addHandlers(); + void addEthHandlers(); + void addNetHandlers(); + void addWeb3Handlers(); + + std::unordered_map m_handlers; +}; +} // namespace bcos::rpc diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp index d2565f4b8f..7efbf97694 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp @@ -26,254 +26,3 @@ using namespace bcos; using namespace bcos::rpc; - -EthEndpoint::EthEndpoint(bcos::rpc::GroupManager::Ptr groupManager) - : m_groupManager(std::move(groupManager)) -{ - initMethod(); -} - -void EthEndpoint::initMethod() -{ - // clang-format off - m_methods[methodString(EthMethod::eth_protocolVersion)] = std::bind(&EthEndpoint::protocolVersionInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_syncing)] = std::bind(&EthEndpoint::syningInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_coinbase)] = std::bind(&EthEndpoint::coinbaseInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_chainId)] = std::bind(&EthEndpoint::chainIdInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_mining)] = std::bind(&EthEndpoint::miningInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_hashrate)] = std::bind(&EthEndpoint::hashrateInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_gasPrice)] = std::bind(&EthEndpoint::gasPriceInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_accounts)] = std::bind(&EthEndpoint::accountsInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_blockNumber)] = std::bind(&EthEndpoint::blockNumberInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_getBalance)] = std::bind(&EthEndpoint::getBalanceInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_getStorageAt)] = std::bind(&EthEndpoint::getStorageAtInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_getTransactionCount)] = std::bind(&EthEndpoint::getTransactionCountInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_getBlockTransactionCountByHash)] = std::bind(&EthEndpoint::getBlockTxCountByHashInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_getBlockTransactionCountByNumber)] = std::bind(&EthEndpoint::getBlockTxCountByNumberInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_getUncleCountByBlockHash)] = std::bind(&EthEndpoint::getUncleCountByBlockHashInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_getUncleCountByBlockNumber)] = std::bind(&EthEndpoint::getUncleCountByBlockNumberInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_getCode)] = std::bind(&EthEndpoint::getCodeInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_sign)] = std::bind(&EthEndpoint::signInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_sendTransaction)] = std::bind(&EthEndpoint::sendTransactionInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_signTransaction)] = std::bind(&EthEndpoint::signTransactionInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_sendRawTransaction)] = std::bind(&EthEndpoint::sendRawTransactionInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_call)] = std::bind(&EthEndpoint::callInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_estimateGas)] = std::bind(&EthEndpoint::estimateGasInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_getBlockByHash)] = std::bind(&EthEndpoint::getBlockByHashInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_getBlockByNumber)] = std::bind(&EthEndpoint::getBlockByNumberInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_getTransactionByHash)] = std::bind(&EthEndpoint::getTransactionByHashInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_getTransactionByBlockHashAndIndex)] = std::bind(&EthEndpoint::getTransactionByBlockHashAndIndexInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_getTransactionByBlockNumberAndIndex)] = std::bind(&EthEndpoint::getTransactionByBlockNumberAndIndexInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_getTransactionReceipt)] = std::bind(&EthEndpoint::getTransactionReceiptInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_getUncleByBlockHashAndIndex)] = std::bind(&EthEndpoint::getUncleByBlockHashAndIndexInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_getUncleByBlockNumberAndIndex)] = std::bind(&EthEndpoint::getUncleByBlockNumberAndIndexInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_newFilter)] = std::bind(&EthEndpoint::newFilterInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_newBlockFilter)] = std::bind(&EthEndpoint::newBlockFilterInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_newPendingTransactionFilter)] = std::bind(&EthEndpoint::newPendingTransactionFilterInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_uninstallFilter)] = std::bind(&EthEndpoint::uninstallFilterInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_getFilterChanges)] = std::bind(&EthEndpoint::getFilterChangesInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_getFilterLogs)] = std::bind(&EthEndpoint::getFilterLogsInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::eth_getLogs)] = std::bind(&EthEndpoint::getLogsInterface, this, std::placeholders::_1, std::placeholders::_2); - // clang-format on - for (auto& [method, _] : m_methods) - { - RPC_IMPL_LOG(INFO) << LOG_BADGE("initMethod") << LOG_KV("method", method); - } - RPC_IMPL_LOG(INFO) << LOG_BADGE("initMethod") << LOG_KV("size", m_methods.size()); -} - -// TODO: error code - -void EthEndpoint::protocolVersion(RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} - -void EthEndpoint::syning(RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::coinbase(RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::chainId(RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::mining(RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::hashrate(RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::gasPrice(RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::accounts(RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::blockNumber(RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::getBalance(std::string_view, std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::getStorageAt(std::string_view, std::string_view, std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::getTransactionCount(std::string_view, std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::getBlockTxCountByHash(std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::getBlockTxCountByNumber(std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::getUncleCountByBlockHash(std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::getUncleCountByBlockNumber(std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::getCode(std::string_view, std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::sign(std::string_view, std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::signTransaction(Json::Value const&, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::sendTransaction(Json::Value const&, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::sendRawTransaction(std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::call(Json::Value const&, std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::estimateGas(Json::Value const&, std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::getBlockByHash(std::string_view, bool, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::getBlockByNumber(std::string_view, bool, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::getTransactionByHash(std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::getTransactionByBlockHashAndIndex( - std::string_view, std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::getTransactionByBlockNumberAndIndex( - std::string_view, std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::getTransactionReceipt(std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::getUncleByBlockHashAndIndex(std::string_view, std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::getUncleByBlockNumberAndIndex(std::string_view, std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::newFilter(Json::Value const&, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::newBlockFilter(RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::newPendingTransactionFilter(RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::uninstallFilter(std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::getFilterChanges(std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::getFilterLogs(std::string_view, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void EthEndpoint::getLogs(Json::Value const&, RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h index 5b69f451aa..be2ca59d30 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h @@ -19,7 +19,6 @@ */ #pragma once -#include "EndpointInterface.h" #include "bcos-rpc/groupmgr/GroupManager.h" #include #include @@ -33,194 +32,54 @@ namespace bcos::rpc /** * eth entry point to match 'eth_' methods */ -class EthEndpoint : public EndpointInterface +class EthEndpoint { public: - explicit EthEndpoint(bcos::rpc::GroupManager::Ptr group_manager); - void initMethod(); - MethodMap&& exportMethods() override { return std::move(m_methods); } - void protocolVersion(RespFunc); - void syning(RespFunc); - void coinbase(RespFunc); - void chainId(RespFunc); - void mining(RespFunc); - void hashrate(RespFunc); - void gasPrice(RespFunc); - void accounts(RespFunc); - void blockNumber(RespFunc); - void getBalance(std::string_view, std::string_view, RespFunc); - void getStorageAt(std::string_view, std::string_view, std::string_view, RespFunc); - void getTransactionCount(std::string_view, std::string_view, RespFunc); - void getBlockTxCountByHash(std::string_view, RespFunc); - void getBlockTxCountByNumber(std::string_view, RespFunc); - void getUncleCountByBlockHash(std::string_view, RespFunc); - void getUncleCountByBlockNumber(std::string_view, RespFunc); - void getCode(std::string_view, std::string_view, RespFunc); - void sign(std::string_view, std::string_view, RespFunc); - void signTransaction(Json::Value const&, RespFunc); - void sendTransaction(Json::Value const&, RespFunc); - void sendRawTransaction(std::string_view, RespFunc); - void call(Json::Value const&, std::string_view, RespFunc); - void estimateGas(Json::Value const&, std::string_view, RespFunc); - void getBlockByHash(std::string_view, bool, RespFunc); - void getBlockByNumber(std::string_view, bool, RespFunc); - void getTransactionByHash(std::string_view, RespFunc); - void getTransactionByBlockHashAndIndex(std::string_view, std::string_view, RespFunc); - void getTransactionByBlockNumberAndIndex(std::string_view, std::string_view, RespFunc); - void getTransactionReceipt(std::string_view, RespFunc); - void getUncleByBlockHashAndIndex(std::string_view, std::string_view, RespFunc); - void getUncleByBlockNumberAndIndex(std::string_view, std::string_view, RespFunc); - void newFilter(Json::Value const&, RespFunc); - void newBlockFilter(RespFunc); - void newPendingTransactionFilter(RespFunc); - void uninstallFilter(std::string_view, RespFunc); - void getFilterChanges(std::string_view, RespFunc); - void getFilterLogs(std::string_view, RespFunc); - void getLogs(Json::Value const&, RespFunc); + explicit EthEndpoint(NodeService::Ptr nodeService) : m_nodeService(std::move(nodeService)) {} + virtual ~EthEndpoint() = default; protected: - std::string_view toView(const Json::Value& value) - { - const char* begin = nullptr; - const char* end = nullptr; - if (!value.getString(&begin, &end)) - { - return {}; - } - std::string_view view(begin, end - begin); - return view; - } - void protocolVersionInterface(Json::Value const&, RespFunc func) - { - protocolVersion(std::move(func)); - } - void syningInterface(Json::Value const&, RespFunc func) { syning(std::move(func)); } - void coinbaseInterface(Json::Value const&, RespFunc func) { coinbase(std::move(func)); } - void chainIdInterface(Json::Value const&, RespFunc func) { chainId(std::move(func)); } - void miningInterface(Json::Value const&, RespFunc func) { mining(std::move(func)); } - void hashrateInterface(Json::Value const&, RespFunc func) { hashrate(std::move(func)); } - void gasPriceInterface(Json::Value const&, RespFunc func) { gasPrice(std::move(func)); } - void accountsInterface(Json::Value const&, RespFunc func) { accounts(std::move(func)); } - void blockNumberInterface(Json::Value const&, RespFunc func) { blockNumber(std::move(func)); } - void getBalanceInterface(Json::Value const& req, RespFunc func) - { - getBalance(toView(req[0U]), toView(req[1U]), std::move(func)); - } - void getStorageAtInterface(Json::Value const& req, RespFunc func) - { - getStorageAt(toView(req[0U]), toView(req[1U]), toView(req[2U]), std::move(func)); - } - void getTransactionCountInterface(Json::Value const& req, RespFunc func) - { - getTransactionCount(toView(req[0U]), toView(req[1U]), std::move(func)); - } - void getBlockTxCountByHashInterface(Json::Value const& req, RespFunc func) - { - getBlockTxCountByHash(toView(req[0U]), std::move(func)); - } - void getBlockTxCountByNumberInterface(Json::Value const& req, RespFunc func) - { - getBlockTxCountByNumber(toView(req[0U]), std::move(func)); - } - void getUncleCountByBlockHashInterface(Json::Value const& req, RespFunc func) - { - getUncleCountByBlockHash(toView(req[0U]), std::move(func)); - } - void getUncleCountByBlockNumberInterface(Json::Value const& req, RespFunc func) - { - getUncleCountByBlockNumber(toView(req[0U]), std::move(func)); - } - void getCodeInterface(Json::Value const& req, RespFunc func) - { - getCode(toView(req[0U]), toView(req[1U]), std::move(func)); - } - void signInterface(Json::Value const& req, RespFunc func) - { - sign(toView(req[0U]), toView(req[1U]), std::move(func)); - } - void signTransactionInterface(Json::Value const& req, RespFunc func) - { - signTransaction(req[0U], std::move(func)); - } - void sendTransactionInterface(Json::Value const& req, RespFunc func) - { - sendTransaction(req[0U], std::move(func)); - } - void sendRawTransactionInterface(Json::Value const& req, RespFunc func) - { - sendRawTransaction(toView(req[0U]), std::move(func)); - } - void callInterface(Json::Value const& req, RespFunc func) - { - call(req[0U], toView(req[1U]), std::move(func)); - } - void estimateGasInterface(Json::Value const& req, RespFunc func) - { - estimateGas(req[0U], toView(req[1U]), std::move(func)); - } - void getBlockByHashInterface(Json::Value const& req, RespFunc func) - { - getBlockByHash(toView(req[0U]), req[1U].asBool(), std::move(func)); - } - void getBlockByNumberInterface(Json::Value const& req, RespFunc func) - { - getBlockByNumber(toView(req[0U]), req[1U].asBool(), std::move(func)); - } - void getTransactionByHashInterface(Json::Value const& req, RespFunc func) - { - getTransactionByHash(toView(req[0U]), std::move(func)); - } - void getTransactionByBlockHashAndIndexInterface(Json::Value const& req, RespFunc func) - { - getTransactionByBlockHashAndIndex(toView(req[0U]), toView(req[1U]), std::move(func)); - } - void getTransactionByBlockNumberAndIndexInterface(Json::Value const& req, RespFunc func) - { - getTransactionByBlockNumberAndIndex(toView(req[0U]), toView(req[1U]), std::move(func)); - } - void getTransactionReceiptInterface(Json::Value const& req, RespFunc func) - { - getTransactionReceipt(toView(req[0U]), std::move(func)); - } - void getUncleByBlockHashAndIndexInterface(Json::Value const& req, RespFunc func) - { - getUncleByBlockHashAndIndex(toView(req[0U]), toView(req[1U]), std::move(func)); - } - void getUncleByBlockNumberAndIndexInterface(Json::Value const& req, RespFunc func) - { - getUncleByBlockNumberAndIndex(toView(req[0U]), toView(req[1U]), std::move(func)); - } - void newFilterInterface(Json::Value const& req, RespFunc func) - { - newFilter(req[0], std::move(func)); - } - void newBlockFilterInterface(Json::Value const&, RespFunc func) - { - newBlockFilter(std::move(func)); - } - void newPendingTransactionFilterInterface(Json::Value const&, RespFunc func) - { - newPendingTransactionFilter(std::move(func)); - } - void uninstallFilterInterface(Json::Value const& req, RespFunc func) - { - uninstallFilter(toView(req[0U]), std::move(func)); - } - void getFilterChangesInterface(Json::Value const& req, RespFunc func) - { - getFilterChanges(toView(req[0U]), std::move(func)); - } - void getFilterLogsInterface(Json::Value const& req, RespFunc func) - { - getFilterLogs(toView(req[0U]), std::move(func)); - } - void getLogsInterface(Json::Value const& req, RespFunc func) - { - getLogs(req[0U], std::move(func)); - } + task::Task protocolVersion(const Json::Value&, Json::Value&); + task::Task syning(const Json::Value&, Json::Value&); + task::Task coinbase(const Json::Value&, Json::Value&); + task::Task chainId(const Json::Value&, Json::Value&); + task::Task mining(const Json::Value&, Json::Value&); + task::Task hashrate(const Json::Value&, Json::Value&); + task::Task gasPrice(const Json::Value&, Json::Value&); + task::Task accounts(const Json::Value&, Json::Value&); + task::Task blockNumber(const Json::Value&, Json::Value&); + task::Task getBalance(const Json::Value&, Json::Value&); + task::Task getStorageAt(const Json::Value&, Json::Value&); + task::Task getTransactionCount(const Json::Value&, Json::Value&); + task::Task getBlockTxCountByHash(const Json::Value&, Json::Value&); + task::Task getBlockTxCountByNumber(const Json::Value&, Json::Value&); + task::Task getUncleCountByBlockHash(const Json::Value&, Json::Value&); + task::Task getUncleCountByBlockNumber(const Json::Value&, Json::Value&); + task::Task getCode(const Json::Value&, Json::Value&); + task::Task sign(const Json::Value&, Json::Value&); + task::Task signTransaction(const Json::Value&, Json::Value&); + task::Task sendTransaction(const Json::Value&, Json::Value&); + task::Task sendRawTransaction(const Json::Value&, Json::Value&); + task::Task call(const Json::Value&, Json::Value&); + task::Task estimateGas(const Json::Value&, Json::Value&); + task::Task getBlockByHash(const Json::Value&, Json::Value&); + task::Task getBlockByNumber(const Json::Value&, Json::Value&); + task::Task getTransactionByHash(const Json::Value&, Json::Value&); + task::Task getTransactionByBlockHashAndIndex(const Json::Value&, Json::Value&); + task::Task getTransactionByBlockNumberAndIndex(const Json::Value&, Json::Value&); + task::Task getTransactionReceipt(const Json::Value&, Json::Value&); + task::Task getUncleByBlockHashAndIndex(const Json::Value&, Json::Value&); + task::Task getUncleByBlockNumberAndIndex(const Json::Value&, Json::Value&); + task::Task newFilter(const Json::Value&, Json::Value&); + task::Task newBlockFilter(const Json::Value&, Json::Value&); + task::Task newPendingTransactionFilter(const Json::Value&, Json::Value&); + task::Task uninstallFilter(const Json::Value&, Json::Value&); + task::Task getFilterChanges(const Json::Value&, Json::Value&); + task::Task getFilterLogs(const Json::Value&, Json::Value&); + task::Task getLogs(const Json::Value&, Json::Value&); private: - bcos::rpc::GroupManager::Ptr m_groupManager; + NodeService::Ptr m_nodeService; }; } // namespace bcos::rpc diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp index 3690e15af3..4ff489ac58 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp @@ -20,38 +20,5 @@ #include "NetEndpoint.h" -#include - using namespace bcos; -using namespace bcos::rpc; - -NetEndpoint::NetEndpoint(bcos::rpc::GroupManager::Ptr groupManager) - : m_groupManager(std::move(groupManager)) -{ - initMethod(); -} - -void NetEndpoint::initMethod() -{ - // clang-format off - m_methods[methodString(EthMethod::net_version)] = std::bind(&NetEndpoint::versionInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::net_listening)] = std::bind(&NetEndpoint::listeningInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::net_peerCount)] = std::bind(&NetEndpoint::peerCountInterface, this, std::placeholders::_1, std::placeholders::_2); - // clang-format on -} - -void NetEndpoint::verison(RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void NetEndpoint::listening(RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void NetEndpoint::peerCount(RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} +using namespace bcos::rpc; \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.h b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.h index 13dedbfb1b..e39b572ac1 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.h @@ -19,7 +19,6 @@ */ #pragma once -#include "EndpointInterface.h" #include "bcos-rpc/groupmgr/GroupManager.h" #include #include @@ -29,23 +28,19 @@ namespace bcos::rpc { -class NetEndpoint : public EndpointInterface +class NetEndpoint { public: - explicit NetEndpoint(bcos::rpc::GroupManager::Ptr groupManager); - void initMethod(); - MethodMap&& exportMethods() override { return std::move(m_methods); } - void verison(RespFunc); - void listening(RespFunc); - void peerCount(RespFunc); + explicit NetEndpoint(NodeService::Ptr nodeService) : m_nodeService(std::move(nodeService)) {} + virtual ~NetEndpoint() = default; protected: - void versionInterface(Json::Value const&, RespFunc func) { verison(std::move(func)); } - void listeningInterface(Json::Value const&, RespFunc func) { listening(std::move(func)); } - void peerCountInterface(Json::Value const&, RespFunc func) { peerCount(std::move(func)); } + task::Task verison(const Json::Value&, Json::Value&); + task::Task listening(const Json::Value&, Json::Value&); + task::Task peerCount(const Json::Value&, Json::Value&); private: - bcos::rpc::GroupManager::Ptr m_groupManager; + NodeService::Ptr m_nodeService; }; } // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp index 19c1e9fbe8..90dad07b48 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp @@ -23,28 +23,3 @@ #include using namespace bcos::rpc; - -Web3Endpoint::Web3Endpoint(bcos::rpc::GroupManager::Ptr groupManager) - : m_groupManager(std::move(groupManager)) -{ - initMethod(); -} - -void Web3Endpoint::initMethod() -{ - // clang-format off - m_methods[methodString(EthMethod::web3_clientVersion)] = std::bind(&Web3Endpoint::clientVersionInterface, this, std::placeholders::_1, std::placeholders::_2); - m_methods[methodString(EthMethod::web3_sha3)] = std::bind(&Web3Endpoint::sha3Interface, this, std::placeholders::_1, std::placeholders::_2); - // clang-format on -} - -void Web3Endpoint::clientVersion(RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} -void Web3Endpoint::sha3(RespFunc func) -{ - Json::Value result; - func(BCOS_ERROR_PTR(MethodNotFound, "This API has not been implemented yet!"), result); -} diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.h b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.h index f905bd3148..670fbc3aed 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.h @@ -19,7 +19,6 @@ */ #pragma once -#include "EndpointInterface.h" #include "bcos-rpc/groupmgr/GroupManager.h" #include #include @@ -28,24 +27,18 @@ namespace bcos::rpc { -class Web3Endpoint : public EndpointInterface +class Web3Endpoint { public: - explicit Web3Endpoint(bcos::rpc::GroupManager::Ptr groupManager); - void initMethod(); - MethodMap&& exportMethods() override { return std::move(m_methods); } - void clientVersion(RespFunc); - void sha3(RespFunc); + explicit Web3Endpoint(NodeService::Ptr nodeService) : m_nodeService(std::move(nodeService)) {} + virtual ~Web3Endpoint() = default; protected: - void clientVersionInterface(Json::Value const&, RespFunc func) - { - clientVersion(std::move(func)); - } - void sha3Interface(Json::Value const&, RespFunc func) { sha3(std::move(func)); }; + task::Task clientVersion(const Json::Value&, Json::Value&); + task::Task sha3(const Json::Value&, Json::Value&); private: - bcos::rpc::GroupManager::Ptr m_groupManager; + NodeService::Ptr m_nodeService; }; } // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-utilities/bcos-utilities/DataConvertUtility.h b/bcos-utilities/bcos-utilities/DataConvertUtility.h index 301d7828c8..738e4245e3 100644 --- a/bcos-utilities/bcos-utilities/DataConvertUtility.h +++ b/bcos-utilities/bcos-utilities/DataConvertUtility.h @@ -20,7 +20,9 @@ #include "Common.h" #include "Error.h" +#include "Ranges.h" #include +#include #include #include #include @@ -47,6 +49,39 @@ Out toHex(const Binary& binary, std::string_view prefix = std::string_view()) return out; } +template +concept Binary = RANGES::contiguous_range; +static std::string toQuantity(const Binary auto& binary) +{ + if (binary.empty()) + { + return "0x0"; + } + auto&& hex = toHex(binary); + auto it = hex.begin(); + while (it != hex.end()) + { + if (*it != '0') + { + break; + } + it++; + } + std::string out = "0x"; + out.reserve(2 + std::distance(it, hex.end())); + out.insert(out.end(), it, hex.end()); + return out; +} + +template +concept Number = std::is_integral_v || std::same_as; +static std::string toQuantity(Number auto number) +{ + std::basic_string bytes(8, '\0'); + boost::endian::store_big_u64(bytes.data(), number); + return toQuantity(bytes); +} + template Out fromHex(const Hex& hex, std::string_view prefix = std::string_view()) { @@ -78,6 +113,11 @@ Out fromHexWithPrefix(const Hex& hex) return fromHex(hex, "0x"); } +inline uint64_t fromQuantity(std::string const& quantity) +{ + return std::stoull(quantity, nullptr, 16); +} + /** * @brief convert the specified bytes data into hex string * From aef40b10eb529bbb4f5bc17e31b4eb8b249dd159 Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Fri, 29 Mar 2024 17:45:01 +0800 Subject: [PATCH 05/39] (rpc): impl web3 rpc handle request. (#4334) --- bcos-rpc/bcos-rpc/Common.h | 21 +++ bcos-rpc/bcos-rpc/Rpc.h | 2 + bcos-rpc/bcos-rpc/RpcFactory.cpp | 1 - bcos-rpc/bcos-rpc/validator/JsonValidator.cpp | 85 ++++++++++ bcos-rpc/bcos-rpc/validator/JsonValidator.h | 38 +++++ .../bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp | 108 +++++++++++++ .../bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h | 33 ++-- .../endpoints/EndpointsMapping.cpp | 86 +++++----- .../web3jsonrpc/endpoints/EthEndpoint.cpp | 153 ++++++++++++++++++ .../web3jsonrpc/endpoints/NetEndpoint.cpp | 14 +- .../web3jsonrpc/endpoints/Web3Endpoint.cpp | 8 + bcos-rpc/test/unittests/common/RPCFixture.h | 5 + bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp | 118 ++++++++++++++ bcos-tool/bcos-tool/NodeConfig.cpp | 2 +- 14 files changed, 619 insertions(+), 55 deletions(-) create mode 100644 bcos-rpc/bcos-rpc/validator/JsonValidator.cpp create mode 100644 bcos-rpc/bcos-rpc/validator/JsonValidator.h create mode 100644 bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp diff --git a/bcos-rpc/bcos-rpc/Common.h b/bcos-rpc/bcos-rpc/Common.h index f0a8cc023b..34e85dbad4 100644 --- a/bcos-rpc/bcos-rpc/Common.h +++ b/bcos-rpc/bcos-rpc/Common.h @@ -18,7 +18,12 @@ * @date 2021-07-02 */ #pragma once +#include "bcos-utilities/Common.h" #include +#include +#include +#include +#include #include #include @@ -33,4 +38,20 @@ enum AMOPClientMessageType AMOP_BROADCAST = 0x112, // 274 AMOP_RESPONSE = 0x113 // 275 }; +class JsonSink +{ +public: + typedef char char_type; + typedef boost::iostreams::sink_tag category; + + explicit JsonSink(bcos::bytes& buffer) : m_buffer(buffer) {} + + std::streamsize write(const char* s, std::streamsize n) + { + m_buffer.insert(m_buffer.end(), (bcos::byte*)s, (bcos::byte*)s + n); + return n; + } + + bcos::bytes& m_buffer; +}; } // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/Rpc.h b/bcos-rpc/bcos-rpc/Rpc.h index 95216e5309..cfbb5f2d22 100644 --- a/bcos-rpc/bcos-rpc/Rpc.h +++ b/bcos-rpc/bcos-rpc/Rpc.h @@ -99,6 +99,8 @@ class Rpc : public RPCInterface, public std::enable_shared_from_this GroupManager::Ptr groupManager() { return m_groupManager; } + bcos::rpc::Web3JsonRpcImpl::Ptr web3JsonRpc() const { return m_web3JsonRpcImpl; } + protected: virtual void notifyGroupInfo(bcos::group::GroupInfo::Ptr _groupInfo); diff --git a/bcos-rpc/bcos-rpc/RpcFactory.cpp b/bcos-rpc/bcos-rpc/RpcFactory.cpp index 872a16217e..7d21749381 100644 --- a/bcos-rpc/bcos-rpc/RpcFactory.cpp +++ b/bcos-rpc/bcos-rpc/RpcFactory.cpp @@ -372,7 +372,6 @@ bcos::rpc::Web3JsonRpcImpl::Ptr RpcFactory::buildWeb3JsonRpc( { auto web3JsonRpc = std::make_shared( m_nodeConfig->groupId(), std::move(_groupManager), m_gateway, _wsService); - web3JsonRpc->setSendTxTimeout(sendTxTimeout); auto httpServer = _wsService->httpServer(); if (httpServer) { diff --git a/bcos-rpc/bcos-rpc/validator/JsonValidator.cpp b/bcos-rpc/bcos-rpc/validator/JsonValidator.cpp new file mode 100644 index 0000000000..da81769a2b --- /dev/null +++ b/bcos-rpc/bcos-rpc/validator/JsonValidator.cpp @@ -0,0 +1,85 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file JsonValidator.cpp + * @author: kyonGuo + * @date 2024/3/28 + */ + +#include "JsonValidator.h" + +namespace bcos::rpc +{ +std::tuple JsonValidator::validate(const Json::Value& root) +{ + if (auto const result{checkRequestFields(root)}; !std::get<0>(result)) + { + return result; + } + // check params specific https://github.com/ethereum/execution-apis + return {true, ""}; +} + +std::tuple JsonValidator::checkRequestFields(const Json::Value& root) +{ + // flag for 4 fileds, maybe twice? + auto flag = 0x1111; + for (auto item = root.begin(); item != root.end(); item++) + { + if (item.name() == "jsonrpc") + { + if (!item->isString()) + { + return {false, "Invalid field: " + item.name()}; + } + flag &= 0x1110; + } + else if (item.name() == "method") + { + if (!item->isString()) + { + return {false, "Invalid field: " + item.name()}; + } + flag &= 0x1101; + } + else if (item.name() == "params") + { + if (!item->isArray()) + { + return {false, "Invalid field: " + item.name()}; + } + flag &= 0x1011; + } + else if (item.name() == "id") + { + if (!item->isInt()) + { + return {false, "Invalid field: " + item.name()}; + } + flag &= 0x0111; + } + else + { + return {false, "Invalid field: " + item.name()}; + } + } + if (flag != 0) + { + return {false, "Request not valid, required fields: jsonrpc, method, params, id"}; + } + return {true, ""}; +} + +} // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/validator/JsonValidator.h b/bcos-rpc/bcos-rpc/validator/JsonValidator.h new file mode 100644 index 0000000000..64300772a8 --- /dev/null +++ b/bcos-rpc/bcos-rpc/validator/JsonValidator.h @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file JsonValidator.h + * @author: kyonGuo + * @date 2024/3/28 + */ + +#pragma once + +#include +#include +#include +#include + + +namespace bcos::rpc +{ +class JsonValidator +{ +public: + static std::tuple validate(const Json::Value& _json); + static std::tuple checkRequestFields(const Json::Value& _json); +}; + +} // namespace bcos::rpc diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp index dae84ee404..88f39a6e59 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp @@ -19,6 +19,114 @@ */ #include "Web3JsonRpcImpl.h" +#include using namespace bcos; using namespace bcos::rpc; + +void Web3JsonRpcImpl::onRPCRequest(std::string_view _requestBody, Sender _sender) +{ + Json::Value request; + Json::Value response; + try + { + if (auto const result{parseRequestAndValidate(_requestBody, request)}; + !std::get(result)) + { + BOOST_THROW_EXCEPTION(JsonRpcException(InvalidRequest, std::get(result))); + } + if (auto const handler{m_endpointsMapping.findHandler(request["method"].asString())}; + handler.has_value()) + { + if (c_fileLogLevel == TRACE) [[unlikely]] + { + RPC_IMPL_LOG(TRACE) << LOG_BADGE("onRPCRequest") << LOG_KV("request", _requestBody); + } + Json::Value const& params = request["params"]; + task::wait([](Web3JsonRpcImpl* self, EndpointsMapping::Handler _handler, + Json::Value _params, Sender sender) -> task::Task { + Json::Value response; + co_await self->handleRequest(_handler, _params, response); + auto&& resp = toBytesResponse(response); + if (c_fileLogLevel == TRACE) [[unlikely]] + { + RPC_IMPL_LOG(TRACE) + << LOG_BADGE("onRPCRequest") + << LOG_KV("response", + std::string_view((const char*)(resp.data()), resp.size())); + } + sender(std::move(resp)); + }(this, handler.value(), std::move(params), std::move(_sender))); + return; + } + BOOST_THROW_EXCEPTION(JsonRpcException(MethodNotFound, "Method not found")); + } + catch (const JsonRpcException& e) + { + buildJsonError(request, e.code(), e.msg(), response); + } + catch (const std::exception& e) + { + buildJsonError(request, InternalError, e.what(), response); + } + auto&& resp = toBytesResponse(response); + RPC_IMPL_LOG(DEBUG) << LOG_BADGE("onRPCRequest") << LOG_DESC("response with exception") + << LOG_KV("request", _requestBody) + << LOG_KV( + "response", std::string_view((const char*)resp.data(), resp.size())); + _sender(std::move(resp)); +} + +task::Task Web3JsonRpcImpl::handleRequest( + EndpointsMapping::Handler handler, Json::Value const& request, Json::Value& response) +{ + try + { + co_await (m_endpoints.*handler)(request, response); + } + catch (std::exception const& e) + { + buildJsonError(request, InternalError, e.what(), response); + } + catch (...) + { + buildJsonError(request, InternalError, "Internal error", response); + } + co_return; +} + + +std::tuple Web3JsonRpcImpl::parseRequestAndValidate( + std::string_view request, Json::Value& root) +{ + if (Json::Reader jsonReader; !jsonReader.parse(request.begin(), request.end(), root)) + { + return {false, "Parse json failed"}; + } + if (auto result{JsonValidator::validate(root)}; !std::get(result)) + { + return result; + } + return {true, ""}; +} + +bcos::bytes Web3JsonRpcImpl::toBytesResponse(Json::Value const& jResp) +{ + std::unique_ptr writer(Json::StreamWriterBuilder().newStreamWriter()); + + bcos::bytes out; + boost::iostreams::stream outputStream(out); + + writer->write(jResp, &outputStream); + writer.reset(); + return out; +} + +void Web3JsonRpcImpl::buildJsonError( + Json::Value const& request, int32_t code, std::string const& message, Json::Value& response) +{ + response["jsonrpc"] = "2.0"; + response["id"] = request["id"]; + response["error"]["code"] = code; + response["error"]["message"] = message; +} diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h index e24c00eaf1..00082913d3 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h @@ -19,12 +19,10 @@ */ #pragma once -#include "bcos-rpc/groupmgr/GroupManager.h" -#include "bcos-rpc/jsonrpc/JsonRpcImpl_2_0.h" -#include "bcos-rpc/validator/CallValidator.h" #include #include -#include +#include +#include #include #include #include @@ -33,21 +31,38 @@ namespace bcos::rpc { -class Web3JsonRpcImpl : public JsonRpcImpl_2_0, public std::enable_shared_from_this +class Web3JsonRpcImpl : public std::enable_shared_from_this { public: + using Ptr = std::shared_ptr; + using Sender = std::function; Web3JsonRpcImpl(std::string _groupId, bcos::rpc::GroupManager::Ptr _groupManager, bcos::gateway::GatewayInterface::Ptr _gatewayInterface, std::shared_ptr _wsService) - : JsonRpcImpl_2_0( - std::move(_groupManager), std::move(_gatewayInterface), std::move(_wsService)), + : m_groupManager(std::move(_groupManager)), + m_gatewayInterface(std::move(_gatewayInterface)), + m_wsService(std::move(_wsService)), m_groupId(std::move(_groupId)), - m_endpoints(_groupManager->getNodeService(m_groupId, "")) + m_endpoints(m_groupManager->getNodeService(m_groupId, "")) {} - ~Web3JsonRpcImpl() override = default; + ~Web3JsonRpcImpl() = default; + + void onRPCRequest(std::string_view _requestBody, Sender _sender); private: + task::Task handleRequest( + EndpointsMapping::Handler handler, Json::Value const& request, Json::Value& response); + static std::tuple parseRequestAndValidate( + std::string_view request, Json::Value& root); + static bcos::bytes toBytesResponse(Json::Value const& jResp); + static void buildJsonError(Json::Value const& request, int32_t code, std::string const& message, + Json::Value& response); + + // Note: only use in one group + GroupManager::Ptr m_groupManager; + bcos::gateway::GatewayInterface::Ptr m_gatewayInterface; + std::shared_ptr m_wsService; std::string m_groupId; Endpoints m_endpoints; EndpointsMapping m_endpointsMapping; diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointsMapping.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointsMapping.cpp index 74a3ee7e18..078654c8fa 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointsMapping.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointsMapping.cpp @@ -52,61 +52,61 @@ void EndpointsMapping::addHandlers() void EndpointsMapping::addEthHandlers() { // clang-format off - // m_handlers[methodString(EthMethod::eth_protocolVersion)] = &Endpoints::protocolVersion; - // m_handlers[methodString(EthMethod::eth_syncing)] = &Endpoints::syning; - // m_handlers[methodString(EthMethod::eth_coinbase)] = &Endpoints::coinbase; - // m_handlers[methodString(EthMethod::eth_chainId)] = &Endpoints::chainId; - // m_handlers[methodString(EthMethod::eth_mining)] = &Endpoints::mining; - // m_handlers[methodString(EthMethod::eth_hashrate)] = &Endpoints::hashrate; - // m_handlers[methodString(EthMethod::eth_gasPrice)] = &Endpoints::gasPrice; - // m_handlers[methodString(EthMethod::eth_accounts)] = &Endpoints::accounts; - // m_handlers[methodString(EthMethod::eth_blockNumber)] = &Endpoints::blockNumber; - // m_handlers[methodString(EthMethod::eth_getBalance)] = &Endpoints::getBalance; - // m_handlers[methodString(EthMethod::eth_getStorageAt)] = &Endpoints::getStorageAt; - // m_handlers[methodString(EthMethod::eth_getTransactionCount)] = &Endpoints::getTransactionCount; - // m_handlers[methodString(EthMethod::eth_getBlockTransactionCountByHash)] = &Endpoints::getBlockTxCountByHash; - // m_handlers[methodString(EthMethod::eth_getBlockTransactionCountByNumber)] = &Endpoints::getBlockTxCountByNumber; - // m_handlers[methodString(EthMethod::eth_getUncleCountByBlockHash)] = &Endpoints::getUncleCountByBlockHash; - // m_handlers[methodString(EthMethod::eth_getUncleCountByBlockNumber)] = &Endpoints::getUncleCountByBlockNumber; - // m_handlers[methodString(EthMethod::eth_getCode)] = &Endpoints::getCode; - // m_handlers[methodString(EthMethod::eth_sign)] = &Endpoints::sign; - // m_handlers[methodString(EthMethod::eth_sendTransaction)] = &Endpoints::sendTransaction; - // m_handlers[methodString(EthMethod::eth_signTransaction)] = &Endpoints::signTransaction; - // m_handlers[methodString(EthMethod::eth_sendRawTransaction)] = &Endpoints::sendRawTransaction; - // m_handlers[methodString(EthMethod::eth_call)] = &Endpoints::call; - // m_handlers[methodString(EthMethod::eth_estimateGas)] = &Endpoints::estimateGas; - // m_handlers[methodString(EthMethod::eth_getBlockByHash)] = &Endpoints::getBlockByHash; - // m_handlers[methodString(EthMethod::eth_getBlockByNumber)] = &Endpoints::getBlockByNumber; - // m_handlers[methodString(EthMethod::eth_getTransactionByHash)] = &Endpoints::getTransactionByHash; - // m_handlers[methodString(EthMethod::eth_getTransactionByBlockHashAndIndex)] = &Endpoints::getTransactionByBlockHashAndIndex; - // m_handlers[methodString(EthMethod::eth_getTransactionByBlockNumberAndIndex)] = &Endpoints::getTransactionByBlockNumberAndIndex; - // m_handlers[methodString(EthMethod::eth_getTransactionReceipt)] = &Endpoints::getTransactionReceipt; - // m_handlers[methodString(EthMethod::eth_getUncleByBlockHashAndIndex)] = &Endpoints::getUncleByBlockHashAndIndex; - // m_handlers[methodString(EthMethod::eth_getUncleByBlockNumberAndIndex)] = &Endpoints::getUncleByBlockNumberAndIndex; - // m_handlers[methodString(EthMethod::eth_newFilter)] = &Endpoints::newFilter; - // m_handlers[methodString(EthMethod::eth_newBlockFilter)] = &Endpoints::newBlockFilter; - // m_handlers[methodString(EthMethod::eth_newPendingTransactionFilter)] = &Endpoints::newPendingTransactionFilter; - // m_handlers[methodString(EthMethod::eth_uninstallFilter)] = &Endpoints::uninstallFilter; - // m_handlers[methodString(EthMethod::eth_getFilterChanges)] = &Endpoints::getFilterChanges; - // m_handlers[methodString(EthMethod::eth_getFilterLogs)] = &Endpoints::getFilterLogs; - // m_handlers[methodString(EthMethod::eth_getLogs)] = &Endpoints::getLogs; + m_handlers[methodString(EthMethod::eth_protocolVersion)] = &Endpoints::protocolVersion; + m_handlers[methodString(EthMethod::eth_syncing)] = &Endpoints::syning; + m_handlers[methodString(EthMethod::eth_coinbase)] = &Endpoints::coinbase; + m_handlers[methodString(EthMethod::eth_chainId)] = &Endpoints::chainId; + m_handlers[methodString(EthMethod::eth_mining)] = &Endpoints::mining; + m_handlers[methodString(EthMethod::eth_hashrate)] = &Endpoints::hashrate; + m_handlers[methodString(EthMethod::eth_gasPrice)] = &Endpoints::gasPrice; + m_handlers[methodString(EthMethod::eth_accounts)] = &Endpoints::accounts; + m_handlers[methodString(EthMethod::eth_blockNumber)] = &Endpoints::blockNumber; + m_handlers[methodString(EthMethod::eth_getBalance)] = &Endpoints::getBalance; + m_handlers[methodString(EthMethod::eth_getStorageAt)] = &Endpoints::getStorageAt; + m_handlers[methodString(EthMethod::eth_getTransactionCount)] = &Endpoints::getTransactionCount; + m_handlers[methodString(EthMethod::eth_getBlockTransactionCountByHash)] = &Endpoints::getBlockTxCountByHash; + m_handlers[methodString(EthMethod::eth_getBlockTransactionCountByNumber)] = &Endpoints::getBlockTxCountByNumber; + m_handlers[methodString(EthMethod::eth_getUncleCountByBlockHash)] = &Endpoints::getUncleCountByBlockHash; + m_handlers[methodString(EthMethod::eth_getUncleCountByBlockNumber)] = &Endpoints::getUncleCountByBlockNumber; + m_handlers[methodString(EthMethod::eth_getCode)] = &Endpoints::getCode; + m_handlers[methodString(EthMethod::eth_sign)] = &Endpoints::sign; + m_handlers[methodString(EthMethod::eth_sendTransaction)] = &Endpoints::sendTransaction; + m_handlers[methodString(EthMethod::eth_signTransaction)] = &Endpoints::signTransaction; + m_handlers[methodString(EthMethod::eth_sendRawTransaction)] = &Endpoints::sendRawTransaction; + m_handlers[methodString(EthMethod::eth_call)] = &Endpoints::call; + m_handlers[methodString(EthMethod::eth_estimateGas)] = &Endpoints::estimateGas; + m_handlers[methodString(EthMethod::eth_getBlockByHash)] = &Endpoints::getBlockByHash; + m_handlers[methodString(EthMethod::eth_getBlockByNumber)] = &Endpoints::getBlockByNumber; + m_handlers[methodString(EthMethod::eth_getTransactionByHash)] = &Endpoints::getTransactionByHash; + m_handlers[methodString(EthMethod::eth_getTransactionByBlockHashAndIndex)] = &Endpoints::getTransactionByBlockHashAndIndex; + m_handlers[methodString(EthMethod::eth_getTransactionByBlockNumberAndIndex)] = &Endpoints::getTransactionByBlockNumberAndIndex; + m_handlers[methodString(EthMethod::eth_getTransactionReceipt)] = &Endpoints::getTransactionReceipt; + m_handlers[methodString(EthMethod::eth_getUncleByBlockHashAndIndex)] = &Endpoints::getUncleByBlockHashAndIndex; + m_handlers[methodString(EthMethod::eth_getUncleByBlockNumberAndIndex)] = &Endpoints::getUncleByBlockNumberAndIndex; + m_handlers[methodString(EthMethod::eth_newFilter)] = &Endpoints::newFilter; + m_handlers[methodString(EthMethod::eth_newBlockFilter)] = &Endpoints::newBlockFilter; + m_handlers[methodString(EthMethod::eth_newPendingTransactionFilter)] = &Endpoints::newPendingTransactionFilter; + m_handlers[methodString(EthMethod::eth_uninstallFilter)] = &Endpoints::uninstallFilter; + m_handlers[methodString(EthMethod::eth_getFilterChanges)] = &Endpoints::getFilterChanges; + m_handlers[methodString(EthMethod::eth_getFilterLogs)] = &Endpoints::getFilterLogs; + m_handlers[methodString(EthMethod::eth_getLogs)] = &Endpoints::getLogs; // clang-format on } void EndpointsMapping::addNetHandlers() { // clang-format off - // m_handlers[methodString(EthMethod::net_version)] = &Endpoints::verison; - // m_handlers[methodString(EthMethod::net_peerCount)] = &Endpoints::peerCount; - // m_handlers[methodString(EthMethod::net_listening)] = &Endpoints::listening; + m_handlers[methodString(EthMethod::net_version)] = &Endpoints::verison; + m_handlers[methodString(EthMethod::net_peerCount)] = &Endpoints::peerCount; + m_handlers[methodString(EthMethod::net_listening)] = &Endpoints::listening; // clang-format on } void EndpointsMapping::addWeb3Handlers() { // clang-format off - //m_handlers[methodString(EthMethod::web3_clientVersion)] = &Endpoints::clientVersion; - //m_handlers[methodString(EthMethod::web3_sha3)] = &Endpoints::sha3; + m_handlers[methodString(EthMethod::web3_clientVersion)] = &Endpoints::clientVersion; + m_handlers[methodString(EthMethod::web3_sha3)] = &Endpoints::sha3; // clang-format on } } // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp index 7efbf97694..3de77fd0f6 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp @@ -26,3 +26,156 @@ using namespace bcos; using namespace bcos::rpc; + +task::Task EthEndpoint::protocolVersion(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::syning(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::coinbase(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::chainId(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::mining(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::hashrate(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::gasPrice(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::accounts(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::blockNumber(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::getBalance(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::getStorageAt(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::getTransactionCount(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::getBlockTxCountByHash(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::getBlockTxCountByNumber(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::getUncleCountByBlockHash(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::getUncleCountByBlockNumber(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::getCode(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::sign(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::signTransaction(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::sendTransaction(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::sendRawTransaction(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::call(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::estimateGas(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::getBlockByHash(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::getBlockByNumber(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::getTransactionByHash(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::getTransactionByBlockHashAndIndex(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::getTransactionByBlockNumberAndIndex(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::getTransactionReceipt(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::getUncleByBlockHashAndIndex(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::getUncleByBlockNumberAndIndex(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::newFilter(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::newBlockFilter(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::newPendingTransactionFilter(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::uninstallFilter(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::getFilterChanges(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::getFilterLogs(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task EthEndpoint::getLogs(const Json::Value&, Json::Value&) +{ + co_return; +} diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp index 4ff489ac58..e305487052 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp @@ -21,4 +21,16 @@ #include "NetEndpoint.h" using namespace bcos; -using namespace bcos::rpc; \ No newline at end of file +using namespace bcos::rpc; +task::Task NetEndpoint::verison(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task NetEndpoint::listening(const Json::Value&, Json::Value&) +{ + co_return; +} +task::Task NetEndpoint::peerCount(const Json::Value&, Json::Value&) +{ + co_return; +} diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp index 90dad07b48..34e81e1d82 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp @@ -23,3 +23,11 @@ #include using namespace bcos::rpc; +bcos::task::Task Web3Endpoint::clientVersion(const Json::Value&, Json::Value&) +{ + co_return; +} +bcos::task::Task Web3Endpoint::sha3(const Json::Value&, Json::Value&) +{ + co_return; +} diff --git a/bcos-rpc/test/unittests/common/RPCFixture.h b/bcos-rpc/test/unittests/common/RPCFixture.h index 5f0a3f4723..d23e1f9ff4 100644 --- a/bcos-rpc/test/unittests/common/RPCFixture.h +++ b/bcos-rpc/test/unittests/common/RPCFixture.h @@ -129,6 +129,11 @@ class RPCFixture : public TestPromptFixture " ; enable compression for p2p message, default: true\n" " ; enable_compression=false\n" "\n" + "[web3_rpc]\n" + " enable=true\n" + " listen_ip=127.0.0.1\n" + " listen_port=8555\n" + "\n" "[rpc]\n" " listen_ip=0.0.0.0\n" " listen_port=20200\n" diff --git a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp new file mode 100644 index 0000000000..f37a7cff25 --- /dev/null +++ b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp @@ -0,0 +1,118 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file Web3RpcTest.cpp + * @author: kyonGuo + * @date 2024/3/29 + */ + + +#include "../common/RPCFixture.h" +#include "bcos-crypto/encrypt/AESCrypto.h" +#include "bcos-crypto/signature/secp256k1/Secp256k1Crypto.h" +#include "bcos-crypto/signature/secp256k1/Secp256k1KeyPair.h" +#include "bcos-crypto/signature/sm2/SM2Crypto.h" +#include "bcos-crypto/signature/sm2/SM2KeyPair.h" +#include "bcos-framework/protocol/GlobalConfig.h" +#include "bcos-rpc/bcos-rpc/RpcFactory.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::rpc; +using namespace bcos::crypto; +namespace bcos::test +{ +class Web3TestFixture : public RPCFixture +{ +public: + Web3TestFixture() + { + rpc = factory->buildLocalRpc(groupInfo, nodeService); + web3JsonRpc = rpc->web3JsonRpc(); + BOOST_CHECK(web3JsonRpc != nullptr); + } + Json::Value onRPCRequestWrapper(std::string_view request) + { + std::promise promise; + web3JsonRpc->onRPCRequest( + request, [&promise](bcos::bytes resp) { promise.set_value(std::move(resp)); }); + Json::Reader reader; + auto jsonBytes = promise.get_future().get(); + std::string_view json((char*)jsonBytes.data(), (char*)jsonBytes.data() + jsonBytes.size()); + Json::Value value; + reader.parse(json.begin(), json.end(), value); + return value; + } + Rpc::Ptr rpc; + Web3JsonRpcImpl::Ptr web3JsonRpc; +}; +BOOST_FIXTURE_TEST_SUITE(testWeb3RPC, Web3TestFixture) +BOOST_AUTO_TEST_CASE(handleInvalidTest) +{ + // no method + { + const auto request = R"({"jsonrpc":"2.0","id":1})"; + auto response = onRPCRequestWrapper(request); + BOOST_CHECK(response.isMember("error")); + BOOST_CHECK(response["error"]["code"].asInt() == InvalidRequest); + } + + // invalid json + { + const auto request = R"({{"jsonrpc":"2.0","id":1, "method":"eth_blockNumber","params":[]})"; + auto response = onRPCRequestWrapper(request); + BOOST_CHECK(response.isMember("error")); + BOOST_CHECK(response["error"]["code"].asInt() == InvalidRequest); + } + + // invalid params type + { + const auto request = R"({"jsonrpc":"2.0","id":1, "method":"eth_blockNumber","params":{}})"; + auto response = onRPCRequestWrapper(request); + BOOST_CHECK(response.isMember("error")); + BOOST_CHECK(response["error"]["code"].asInt() == InvalidRequest); + } + + // invalid method + { + const auto request = R"({"jsonrpc":"2.0","id":1, "method":"eth_AAA","params":[]})"; + auto response = onRPCRequestWrapper(request); + BOOST_CHECK(response.isMember("error")); + BOOST_CHECK(response["error"]["code"].asInt() == MethodNotFound); + BOOST_CHECK(response["error"]["message"].asString() == "Method not found"); + } +} + +BOOST_AUTO_TEST_CASE(handleValidTest) +{ + // method + { + const auto request = R"({"jsonrpc":"2.0","id":1, "method":"eth_blockNumber","params":[]})"; + auto response = onRPCRequestWrapper(request); + BOOST_CHECK(!response.isMember("error")); + } +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test \ No newline at end of file diff --git a/bcos-tool/bcos-tool/NodeConfig.cpp b/bcos-tool/bcos-tool/NodeConfig.cpp index 69d4631bed..b49f00447d 100644 --- a/bcos-tool/bcos-tool/NodeConfig.cpp +++ b/bcos-tool/bcos-tool/NodeConfig.cpp @@ -384,7 +384,7 @@ void NodeConfig::loadWeb3RpcConfig(boost::property_tree::ptree const& _pt) { /* [web3_rpc] - enble=false + enable=false listen_ip=0.0.0.0 listen_port=8545 thread_count=16 From b6a5cf4a1e15bd73d207f92056d146f885d0c28e Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Tue, 2 Apr 2024 12:37:49 +0800 Subject: [PATCH 06/39] (rpc): impl eth/web3/net namespace fields functions. (#4337) --- bcos-framework/bcos-framework/ledger/Ledger.h | 8 + bcos-ledger/src/libledger/LedgerMethods.cpp | 42 ++++ bcos-ledger/src/libledger/LedgerMethods.h | 3 + bcos-rpc/CMakeLists.txt | 2 +- .../bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp | 67 ++---- .../bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h | 8 +- .../endpoints/EndpointsMapping.cpp | 2 +- .../web3jsonrpc/endpoints/EthEndpoint.cpp | 217 ++++++++++++++++-- .../web3jsonrpc/endpoints/EthEndpoint.h | 8 +- .../web3jsonrpc/endpoints/NetEndpoint.cpp | 12 +- .../web3jsonrpc/endpoints/Web3Endpoint.cpp | 24 +- bcos-rpc/bcos-rpc/web3jsonrpc/utils/Common.h | 33 +++ bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.cpp | 41 ++++ bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.h | 41 ++++ bcos-rpc/test/CMakeLists.txt | 5 +- bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp | 100 +++++++- 16 files changed, 524 insertions(+), 89 deletions(-) create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/utils/Common.h create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.cpp create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.h diff --git a/bcos-framework/bcos-framework/ledger/Ledger.h b/bcos-framework/bcos-framework/ledger/Ledger.h index c8042fd1c2..07e3974f5c 100644 --- a/bcos-framework/bcos-framework/ledger/Ledger.h +++ b/bcos-framework/bcos-framework/ledger/Ledger.h @@ -81,6 +81,14 @@ inline constexpr struct GetBlockHash } } getBlockHash{}; +inline constexpr struct GetBlockNumber +{ + task::Task operator()(auto& ledger, crypto::HashType hash) const + { + co_return co_await tag_invoke(*this, ledger, std::move(hash)); + } +} getBlockNumber{}; + using SystemConfigEntry = std::tuple; inline constexpr struct GetSystemConfig { diff --git a/bcos-ledger/src/libledger/LedgerMethods.cpp b/bcos-ledger/src/libledger/LedgerMethods.cpp index 9033e25a94..03833538ff 100644 --- a/bcos-ledger/src/libledger/LedgerMethods.cpp +++ b/bcos-ledger/src/libledger/LedgerMethods.cpp @@ -220,6 +220,48 @@ bcos::task::Task bcos::ledger::tag_invoke( Awaitable awaitable{.m_ledger = ledger, .m_blockNumber = blockNumber, .m_result = {}}; co_return co_await awaitable; } + +bcos::task::Task bcos::ledger::tag_invoke( + bcos::ledger::tag_t /*unused*/, + bcos::ledger::LedgerInterface& ledger, bcos::crypto::HashType hash) +{ + struct Awaitable + { + bcos::ledger::LedgerInterface& m_ledger; + bcos::crypto::HashType m_hash; + + std::variant m_result; + + constexpr static bool await_ready() noexcept { return false; } + void await_suspend(CO_STD::coroutine_handle<> handle) + { + m_ledger.asyncGetBlockNumberByHash( + m_hash, [this, handle](bcos::Error::Ptr error, bcos::protocol::BlockNumber number) { + if (error) + { + m_result.emplace(std::move(error)); + } + else + { + m_result.emplace(number); + } + handle.resume(); + }); + } + bcos::protocol::BlockNumber await_resume() + { + if (std::holds_alternative(m_result)) + { + BOOST_THROW_EXCEPTION(*std::get(m_result)); + } + return std::get(m_result); + } + }; + + Awaitable awaitable{.m_ledger = ledger, .m_hash = std::move(hash), .m_result = {}}; + co_return co_await awaitable; +} + bcos::task::Task bcos::ledger::tag_invoke( ledger::tag_t /*unused*/, LedgerInterface& ledger, std::string_view key) { diff --git a/bcos-ledger/src/libledger/LedgerMethods.h b/bcos-ledger/src/libledger/LedgerMethods.h index b25afb4d8f..b2c77cea53 100644 --- a/bcos-ledger/src/libledger/LedgerMethods.h +++ b/bcos-ledger/src/libledger/LedgerMethods.h @@ -75,6 +75,9 @@ task::Task tag_invoke( task::Task tag_invoke(ledger::tag_t /*unused*/, LedgerInterface& ledger, protocol::BlockNumber blockNumber); +task::Task tag_invoke( + ledger::tag_t /*unused*/, LedgerInterface& ledger, crypto::HashType hash); + task::Task tag_invoke( ledger::tag_t /*unused*/, LedgerInterface& ledger, std::string_view key); diff --git a/bcos-rpc/CMakeLists.txt b/bcos-rpc/CMakeLists.txt index d66fdd3bc4..8c6bfc1002 100644 --- a/bcos-rpc/CMakeLists.txt +++ b/bcos-rpc/CMakeLists.txt @@ -29,7 +29,7 @@ file(GLOB_RECURSE SRCS bcos-rpc/*.cpp) find_package(tarscpp REQUIRED) add_library(${RPC_TARGET} ${SRCS} ${HEADERS}) -target_link_libraries(${RPC_TARGET} PUBLIC bcos-boostssl ${CRYPTO_TARGET} ${TARS_PROTOCOL_TARGET} jsoncpp_static ${CRYPTO_TARGET} tarscpp::tarsservant tarscpp::tarsutil) +target_link_libraries(${RPC_TARGET} PUBLIC bcos-boostssl ${LEDGER_TARGET} ${CRYPTO_TARGET} ${TARS_PROTOCOL_TARGET} jsoncpp_static tarscpp::tarsservant tarscpp::tarsutil) if (TESTS) enable_testing() diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp index 88f39a6e59..af80554890 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp @@ -30,33 +30,33 @@ void Web3JsonRpcImpl::onRPCRequest(std::string_view _requestBody, Sender _sender Json::Value response; try { - if (auto const result{parseRequestAndValidate(_requestBody, request)}; - !std::get(result)) + if (auto const& [valid, msg] = parseRequestAndValidate(_requestBody, request); !valid) { - BOOST_THROW_EXCEPTION(JsonRpcException(InvalidRequest, std::get(result))); + BOOST_THROW_EXCEPTION(JsonRpcException(InvalidRequest, msg)); } - if (auto const handler{m_endpointsMapping.findHandler(request["method"].asString())}; + if (auto const handler = m_endpointsMapping.findHandler(request["method"].asString()); handler.has_value()) { if (c_fileLogLevel == TRACE) [[unlikely]] { RPC_IMPL_LOG(TRACE) << LOG_BADGE("onRPCRequest") << LOG_KV("request", _requestBody); } - Json::Value const& params = request["params"]; task::wait([](Web3JsonRpcImpl* self, EndpointsMapping::Handler _handler, - Json::Value _params, Sender sender) -> task::Task { - Json::Value response; - co_await self->handleRequest(_handler, _params, response); - auto&& resp = toBytesResponse(response); + Json::Value _request, Sender sender) -> task::Task { + Json::Value const& params = _request["params"]; + Json::Value resp; + co_await (self->m_endpoints.*_handler)(params, resp); + resp["id"] = _request["id"]; + auto&& respBytes = toBytesResponse(resp); if (c_fileLogLevel == TRACE) [[unlikely]] { RPC_IMPL_LOG(TRACE) << LOG_BADGE("onRPCRequest") << LOG_KV("response", - std::string_view((const char*)(resp.data()), resp.size())); + std::string_view((const char*)(respBytes.data()), respBytes.size())); } - sender(std::move(resp)); - }(this, handler.value(), std::move(params), std::move(_sender))); + sender(std::move(respBytes)); + }(this, handler.value(), std::move(request), std::move(_sender))); return; } BOOST_THROW_EXCEPTION(JsonRpcException(MethodNotFound, "Method not found")); @@ -65,10 +65,18 @@ void Web3JsonRpcImpl::onRPCRequest(std::string_view _requestBody, Sender _sender { buildJsonError(request, e.code(), e.msg(), response); } + catch (bcos::Error const& e) + { + buildJsonError(request, InternalError, e.errorMessage(), response); + } catch (const std::exception& e) { buildJsonError(request, InternalError, e.what(), response); } + catch (...) + { + buildJsonError(request, InternalError, "Internal error", response); + } auto&& resp = toBytesResponse(response); RPC_IMPL_LOG(DEBUG) << LOG_BADGE("onRPCRequest") << LOG_DESC("response with exception") << LOG_KV("request", _requestBody) @@ -77,25 +85,6 @@ void Web3JsonRpcImpl::onRPCRequest(std::string_view _requestBody, Sender _sender _sender(std::move(resp)); } -task::Task Web3JsonRpcImpl::handleRequest( - EndpointsMapping::Handler handler, Json::Value const& request, Json::Value& response) -{ - try - { - co_await (m_endpoints.*handler)(request, response); - } - catch (std::exception const& e) - { - buildJsonError(request, InternalError, e.what(), response); - } - catch (...) - { - buildJsonError(request, InternalError, "Internal error", response); - } - co_return; -} - - std::tuple Web3JsonRpcImpl::parseRequestAndValidate( std::string_view request, Json::Value& root) { @@ -112,7 +101,10 @@ std::tuple Web3JsonRpcImpl::parseRequestAndValidate( bcos::bytes Web3JsonRpcImpl::toBytesResponse(Json::Value const& jResp) { - std::unique_ptr writer(Json::StreamWriterBuilder().newStreamWriter()); + auto builder = Json::StreamWriterBuilder(); + builder["commentStyle"] = "None"; + builder["indentation"] = ""; + std::unique_ptr writer(builder.newStreamWriter()); bcos::bytes out; boost::iostreams::stream outputStream(out); @@ -120,13 +112,4 @@ bcos::bytes Web3JsonRpcImpl::toBytesResponse(Json::Value const& jResp) writer->write(jResp, &outputStream); writer.reset(); return out; -} - -void Web3JsonRpcImpl::buildJsonError( - Json::Value const& request, int32_t code, std::string const& message, Json::Value& response) -{ - response["jsonrpc"] = "2.0"; - response["id"] = request["id"]; - response["error"]["code"] = code; - response["error"]["message"] = message; -} +} \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h index 00082913d3..7532e6887a 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h @@ -19,6 +19,8 @@ */ #pragma once +#include + #include #include #include @@ -50,15 +52,9 @@ class Web3JsonRpcImpl : public std::enable_shared_from_this void onRPCRequest(std::string_view _requestBody, Sender _sender); private: - task::Task handleRequest( - EndpointsMapping::Handler handler, Json::Value const& request, Json::Value& response); static std::tuple parseRequestAndValidate( std::string_view request, Json::Value& root); static bcos::bytes toBytesResponse(Json::Value const& jResp); - static void buildJsonError(Json::Value const& request, int32_t code, std::string const& message, - Json::Value& response); - - // Note: only use in one group GroupManager::Ptr m_groupManager; bcos::gateway::GatewayInterface::Ptr m_gatewayInterface; diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointsMapping.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointsMapping.cpp index 078654c8fa..df4797392f 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointsMapping.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointsMapping.cpp @@ -53,7 +53,7 @@ void EndpointsMapping::addEthHandlers() { // clang-format off m_handlers[methodString(EthMethod::eth_protocolVersion)] = &Endpoints::protocolVersion; - m_handlers[methodString(EthMethod::eth_syncing)] = &Endpoints::syning; + m_handlers[methodString(EthMethod::eth_syncing)] = &Endpoints::syncing; m_handlers[methodString(EthMethod::eth_coinbase)] = &Endpoints::coinbase; m_handlers[methodString(EthMethod::eth_chainId)] = &Endpoints::chainId; m_handlers[methodString(EthMethod::eth_mining)] = &Endpoints::mining; diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp index 3de77fd0f6..94adc33875 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp @@ -23,159 +23,340 @@ #include #include #include +#include +#include using namespace bcos; using namespace bcos::rpc; task::Task EthEndpoint::protocolVersion(const Json::Value&, Json::Value&) { + BOOST_THROW_EXCEPTION( + JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); co_return; } -task::Task EthEndpoint::syning(const Json::Value&, Json::Value&) +task::Task EthEndpoint::syncing(const Json::Value&, Json::Value& response) { + Json::Value result = false; + buildJsonContent(result, response); co_return; } task::Task EthEndpoint::coinbase(const Json::Value&, Json::Value&) { + BOOST_THROW_EXCEPTION( + JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); co_return; } -task::Task EthEndpoint::chainId(const Json::Value&, Json::Value&) +task::Task EthEndpoint::chainId(const Json::Value&, Json::Value& response) { + // TODO: get chain id + Json::Value result = "0x4ee8"; // 20200 + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::mining(const Json::Value&, Json::Value&) +task::Task EthEndpoint::mining(const Json::Value&, Json::Value& response) { + Json::Value result = false; + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::hashrate(const Json::Value&, Json::Value&) +task::Task EthEndpoint::hashrate(const Json::Value&, Json::Value& response) { + Json::Value result = "0x0"; + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::gasPrice(const Json::Value&, Json::Value&) +task::Task EthEndpoint::gasPrice(const Json::Value&, Json::Value& response) { + // result: gasPrice(QTY) + auto ledger = m_nodeService->ledger(); + // TODO: to check gas price is hex value or not + auto [gasPrice, _] = co_await ledger::getSystemConfig(*ledger, ledger::SYSTEM_KEY_TX_GAS_PRICE); + auto value = std::stoull(gasPrice); + Json::Value result = toQuantity(value); + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::accounts(const Json::Value&, Json::Value&) +task::Task EthEndpoint::accounts(const Json::Value&, Json::Value& response) { + Json::Value result = Json::arrayValue; + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::blockNumber(const Json::Value&, Json::Value&) +task::Task EthEndpoint::blockNumber(const Json::Value&, Json::Value& response) { + auto ledger = m_nodeService->ledger(); + auto number = co_await ledger::getCurrentBlockNumber(*ledger); + Json::Value result = toQuantity(number); + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::getBalance(const Json::Value&, Json::Value&) +task::Task EthEndpoint::getBalance(const Json::Value& request, Json::Value& response) { + // params: address(DATA), blockNumber(QTY|TAG) + // result: balance(QTY) + // FIXME: get balance from ledger co_return; } task::Task EthEndpoint::getStorageAt(const Json::Value&, Json::Value&) { + // params: address(DATA), position(QTY), blockNumber(QTY|TAG) + // result: value(DATA) + BOOST_THROW_EXCEPTION( + JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); co_return; } -task::Task EthEndpoint::getTransactionCount(const Json::Value&, Json::Value&) +task::Task EthEndpoint::getTransactionCount(const Json::Value& request, Json::Value& response) { + // params: address(DATA), blockNumber(QTY|TAG) + // result: nonce(QTY) + // FIXME: impliment getTransactionCount co_return; } -task::Task EthEndpoint::getBlockTxCountByHash(const Json::Value&, Json::Value&) +task::Task EthEndpoint::getBlockTxCountByHash( + const Json::Value& request, Json::Value& response) { + // params: blockHash(DATA) + // result: transactionCount(QTY) + auto const hashStr = toView(request[0U]); + auto hash = crypto::HashType(hashStr, crypto::HashType::FromHex); + auto const ledger = m_nodeService->ledger(); + auto number = co_await ledger::getBlockNumber(*ledger, std::move(hash)); + auto block = co_await ledger::getBlockData(*ledger, number, bcos::ledger::TRANSACTIONS_HASH); + Json::Value result = toQuantity(block->transactionsHashSize()); + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::getBlockTxCountByNumber(const Json::Value&, Json::Value&) +task::Task EthEndpoint::getBlockTxCountByNumber( + const Json::Value& request, Json::Value& response) { + // params: blockNumber(QTY|TAG) + // result: transactionCount(QTY) + auto const number = fromQuantity(std::string(toView(request[0U]))); + auto const ledger = m_nodeService->ledger(); + auto block = co_await ledger::getBlockData(*ledger, number, bcos::ledger::TRANSACTIONS_HASH); + Json::Value result = toQuantity(block->transactionsHashSize()); + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::getUncleCountByBlockHash(const Json::Value&, Json::Value&) +task::Task EthEndpoint::getUncleCountByBlockHash( + const Json::Value& request, Json::Value& response) { + Json::Value result = "0x0"; + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::getUncleCountByBlockNumber(const Json::Value&, Json::Value&) +task::Task EthEndpoint::getUncleCountByBlockNumber( + const Json::Value& request, Json::Value& response) { + Json::Value result = "0x0"; + buildJsonContent(result, response); co_return; } task::Task EthEndpoint::getCode(const Json::Value&, Json::Value&) { + // params: address(DATA), blockNumber(QTY|TAG) + // result: code(DATA) + BOOST_THROW_EXCEPTION( + JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); co_return; } task::Task EthEndpoint::sign(const Json::Value&, Json::Value&) { + // params: address(DATA), message(DATA) + // result: signature(DATA) + BOOST_THROW_EXCEPTION( + JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); co_return; } task::Task EthEndpoint::signTransaction(const Json::Value&, Json::Value&) { + // params: transaction(TX), address(DATA) + // result: signedTransaction(DATA) + BOOST_THROW_EXCEPTION( + JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); co_return; } task::Task EthEndpoint::sendTransaction(const Json::Value&, Json::Value&) { + // params: transaction(TX) + // result: transactionHash(DATA) + BOOST_THROW_EXCEPTION( + JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); co_return; } -task::Task EthEndpoint::sendRawTransaction(const Json::Value&, Json::Value&) +task::Task EthEndpoint::sendRawTransaction(const Json::Value& request, Json::Value& response) { + // params: signedTransaction(DATA) + // result: transactionHash(DATA) + // FIXME: impl this co_return; } -task::Task EthEndpoint::call(const Json::Value&, Json::Value&) +task::Task EthEndpoint::call(const Json::Value& request, Json::Value& response) { + // params: transaction(TX), blockNumber(QTY|TAG) + // result: data(DATA) + // FIXME: impl this co_return; } -task::Task EthEndpoint::estimateGas(const Json::Value&, Json::Value&) +task::Task EthEndpoint::estimateGas(const Json::Value& request, Json::Value& response) { + // params: transaction(TX), blockNumber(QTY|TAG) + // result: gas(QTY) + Json::Value result = "0x0"; + buildJsonContent(result, response); co_return; } task::Task EthEndpoint::getBlockByHash(const Json::Value&, Json::Value&) { + // params: blockHash(DATA), fullTransaction(Boolean) + // result: block(BLOCK) + BOOST_THROW_EXCEPTION( + JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); co_return; } task::Task EthEndpoint::getBlockByNumber(const Json::Value&, Json::Value&) { + // params: blockNumber(QTY|TAG), fullTransaction(Boolean) + // result: block(BLOCK) + BOOST_THROW_EXCEPTION( + JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); co_return; } task::Task EthEndpoint::getTransactionByHash(const Json::Value&, Json::Value&) { + // params: transactionHash(DATA) + // result: transaction(TX) + BOOST_THROW_EXCEPTION( + JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); co_return; } task::Task EthEndpoint::getTransactionByBlockHashAndIndex(const Json::Value&, Json::Value&) { + // params: blockHash(DATA), transactionIndex(QTY) + // result: transaction(TX) + BOOST_THROW_EXCEPTION( + JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); co_return; } task::Task EthEndpoint::getTransactionByBlockNumberAndIndex(const Json::Value&, Json::Value&) { + // params: blockNumber(QTY|TAG), transactionIndex(QTY) + // result: transaction(TX) + BOOST_THROW_EXCEPTION( + JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); co_return; } task::Task EthEndpoint::getTransactionReceipt(const Json::Value&, Json::Value&) { + // params: transactionHash(DATA) + // result: transactionReceipt(RECEIPT) + + BOOST_THROW_EXCEPTION( + JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); + co_return; } -task::Task EthEndpoint::getUncleByBlockHashAndIndex(const Json::Value&, Json::Value&) +task::Task EthEndpoint::getUncleByBlockHashAndIndex(const Json::Value&, Json::Value& response) { + Json::Value result = "null"; + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::getUncleByBlockNumberAndIndex(const Json::Value&, Json::Value&) +task::Task EthEndpoint::getUncleByBlockNumberAndIndex( + const Json::Value&, Json::Value& response) { + Json::Value result = "null"; + buildJsonContent(result, response); co_return; } task::Task EthEndpoint::newFilter(const Json::Value&, Json::Value&) { + // params: filter(FILTER) + // result: filterId(QTY) + BOOST_THROW_EXCEPTION( + JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); + co_return; } task::Task EthEndpoint::newBlockFilter(const Json::Value&, Json::Value&) { + // result: filterId(QTY) + BOOST_THROW_EXCEPTION( + JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); + co_return; } task::Task EthEndpoint::newPendingTransactionFilter(const Json::Value&, Json::Value&) { + // result: filterId(QTY) + BOOST_THROW_EXCEPTION( + JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); + co_return; } task::Task EthEndpoint::uninstallFilter(const Json::Value&, Json::Value&) { + // params: filterId(QTY) + // result: success(Boolean) + BOOST_THROW_EXCEPTION( + JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); + co_return; } task::Task EthEndpoint::getFilterChanges(const Json::Value&, Json::Value&) { + // params: filterId(QTY) + // result: logs(ARRAY) + BOOST_THROW_EXCEPTION( + JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); + co_return; } task::Task EthEndpoint::getFilterLogs(const Json::Value&, Json::Value&) { + // params: filterId(QTY) + // result: logs(ARRAY) + BOOST_THROW_EXCEPTION( + JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); + co_return; } task::Task EthEndpoint::getLogs(const Json::Value&, Json::Value&) { + // params: filter(FILTER) + // result: logs(ARRAY) + BOOST_THROW_EXCEPTION( + JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); + co_return; } + +task::Task> EthEndpoint::getBlockNumberByTag( + std::string_view blockTag) +{ + if (blockTag == EarliestBlock) + { + co_return std::make_tuple(0, false); + } + else if (blockTag == LatestBlock || blockTag == SafeBlock || blockTag == FinalizedBlock || + blockTag == PendingBlock) + { + auto ledger = m_nodeService->ledger(); + auto number = co_await ledger::getCurrentBlockNumber(*ledger); + co_return std::make_tuple(number, true); + } + else + { + static const std::regex hexRegex("^0x[0-9a-fA-F]+$"); + if (std::regex_match(blockTag.data(), hexRegex)) + { + auto ledger = m_nodeService->ledger(); + auto number = co_await ledger::getCurrentBlockNumber(*ledger); + auto blockNumber = fromQuantity(std::string(blockTag)); + co_return std::make_tuple(blockNumber, std::cmp_equal(number, blockNumber)); + } + BOOST_THROW_EXCEPTION( + JsonRpcException(InvalidParams, "Invalid Block Number: " + std::string(blockTag))); + } +} diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h index be2ca59d30..2c7306040d 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h @@ -19,7 +19,9 @@ */ #pragma once -#include "bcos-rpc/groupmgr/GroupManager.h" +#include +#include +#include #include #include #include @@ -40,7 +42,7 @@ class EthEndpoint protected: task::Task protocolVersion(const Json::Value&, Json::Value&); - task::Task syning(const Json::Value&, Json::Value&); + task::Task syncing(const Json::Value&, Json::Value&); task::Task coinbase(const Json::Value&, Json::Value&); task::Task chainId(const Json::Value&, Json::Value&); task::Task mining(const Json::Value&, Json::Value&); @@ -77,6 +79,8 @@ class EthEndpoint task::Task getFilterChanges(const Json::Value&, Json::Value&); task::Task getFilterLogs(const Json::Value&, Json::Value&); task::Task getLogs(const Json::Value&, Json::Value&); + task::Task> getBlockNumberByTag( + std::string_view blockTag); private: NodeService::Ptr m_nodeService; diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp index e305487052..6228992019 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp @@ -20,17 +20,25 @@ #include "NetEndpoint.h" +#include + using namespace bcos; using namespace bcos::rpc; -task::Task NetEndpoint::verison(const Json::Value&, Json::Value&) +task::Task NetEndpoint::verison(const Json::Value& request, Json::Value& response) { + // TODO: get chain id + Json::Value result = "0x4ee8"; // 20200 + buildJsonContent(result, response); co_return; } -task::Task NetEndpoint::listening(const Json::Value&, Json::Value&) +task::Task NetEndpoint::listening(const Json::Value& request, Json::Value& response) { + Json::Value result = true; + buildJsonContent(result, response); co_return; } task::Task NetEndpoint::peerCount(const Json::Value&, Json::Value&) { + // TODO: get gateway peer co_return; } diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp index 34e81e1d82..cd572c53d5 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp @@ -19,15 +19,29 @@ */ #include "Web3Endpoint.h" - -#include - +#include "include/BuildInfo.h" +#include +#include using namespace bcos::rpc; -bcos::task::Task Web3Endpoint::clientVersion(const Json::Value&, Json::Value&) +bcos::task::Task Web3Endpoint::clientVersion( + const Json::Value& request, Json::Value& response) { + std::string version = "FISCO-BCOS-Web3RPC/" + std::string(FISCO_BCOS_PROJECT_VERSION) + "-" + + std::string(FISCO_BCOS_BUILD_TYPE) + "/" + + std::string(FISCO_BCOS_BUILD_PLATFORM) + "/" + + std::string(FISCO_BCOS_COMMIT_HASH); + Json::Value result = version; + buildJsonContent(result, response); co_return; } -bcos::task::Task Web3Endpoint::sha3(const Json::Value&, Json::Value&) +bcos::task::Task Web3Endpoint::sha3(const Json::Value& request, Json::Value& response) { + auto msg = toView(request[0U]); + crypto::hasher::openssl::OpenSSL_Keccak256_Hasher hasher; + hasher.update(bytesConstRef((byte*)msg.data(), msg.size())); + crypto::HashType out; + hasher.final(out); + Json::Value result = out.hexPrefixed(); + buildJsonContent(result, response); co_return; } diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/utils/Common.h b/bcos-rpc/bcos-rpc/web3jsonrpc/utils/Common.h new file mode 100644 index 0000000000..09a32f6281 --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/utils/Common.h @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file Common.h + * @author: kyonGuo + * @date 2024/3/29 + */ + +#pragma once + +#include +#include + +namespace bcos::rpc +{ +constexpr const std::string_view EarliestBlock{"earliest"}; +constexpr const std::string_view LatestBlock{"latest"}; +constexpr const std::string_view PendingBlock{"pending"}; +constexpr const std::string_view SafeBlock{"safe"}; +constexpr const std::string_view FinalizedBlock{"finalized"}; +} // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.cpp new file mode 100644 index 0000000000..e477409ebe --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.cpp @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file util.cpp + * @author: kyonGuo + * @date 2024/3/29 + */ + +#include "util.h" + +using namespace bcos::rpc; + +void bcos::rpc::buildJsonContent(Json::Value& result, Json::Value& response) +{ + response["jsonrpc"] = "2.0"; + response["result"].swap(result); +} + +void bcos::rpc::buildJsonError( + Json::Value const& request, int32_t code, std::string message, Json::Value& response) +{ + response["jsonrpc"] = "2.0"; + // maybe request not init + response["id"] = request.isMember("id") ? request["id"] : "null"; + Json::Value error; + error["code"] = code; + error["message"] = std::move(message); + response["error"] = std::move(error); +} diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.h b/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.h new file mode 100644 index 0000000000..b59d88af6c --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.h @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file util.h + * @author: kyonGuo + * @date 2024/3/29 + */ + +#pragma once +#include +#include + +namespace bcos::rpc +{ +void buildJsonContent(Json::Value& result, Json::Value& response); +void buildJsonError( + Json::Value const& request, int32_t code, std::string message, Json::Value& response); +inline std::string_view toView(const Json::Value& value) +{ + const char* begin = nullptr; + const char* end = nullptr; + if (!value.getString(&begin, &end)) + { + return {}; + } + std::string_view view(begin, end - begin); + return view; +} +} // namespace bcos::rpc diff --git a/bcos-rpc/test/CMakeLists.txt b/bcos-rpc/test/CMakeLists.txt index d8e257ac3d..1b3783b7d3 100644 --- a/bcos-rpc/test/CMakeLists.txt +++ b/bcos-rpc/test/CMakeLists.txt @@ -26,5 +26,6 @@ add_executable(${TEST_BINARY_NAME} ${SOURCES}) target_include_directories(${TEST_BINARY_NAME} PRIVATE .) target_compile_options(${TEST_BINARY_NAME} PRIVATE -Wno-unused-variable) -target_link_libraries(${TEST_BINARY_NAME} ${RPC_TARGET} ${TOOL_TARGET} Boost::unit_test_framework) -add_test(NAME ${TEST_BINARY_NAME} WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${TEST_BINARY_NAME}) \ No newline at end of file +target_link_libraries(${TEST_BINARY_NAME} ${LEDGER_TARGET} ${RPC_TARGET} ${TOOL_TARGET} Boost::unit_test_framework) +#add_test(NAME ${TEST_BINARY_NAME} WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${TEST_BINARY_NAME}) +config_test_cases("" "${SOURCES}" ${TEST_BINARY_NAME} "") \ No newline at end of file diff --git a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp index f37a7cff25..dee6290989 100644 --- a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp +++ b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp @@ -55,14 +55,22 @@ class Web3TestFixture : public RPCFixture } Json::Value onRPCRequestWrapper(std::string_view request) { - std::promise promise; - web3JsonRpc->onRPCRequest( - request, [&promise](bcos::bytes resp) { promise.set_value(std::move(resp)); }); - Json::Reader reader; - auto jsonBytes = promise.get_future().get(); - std::string_view json((char*)jsonBytes.data(), (char*)jsonBytes.data() + jsonBytes.size()); Json::Value value; - reader.parse(json.begin(), json.end(), value); + try + { + std::promise promise; + web3JsonRpc->onRPCRequest( + request, [&promise](bcos::bytes resp) { promise.set_value(std::move(resp)); }); + Json::Reader reader; + auto jsonBytes = promise.get_future().get(); + std::string_view json( + (char*)jsonBytes.data(), (char*)jsonBytes.data() + jsonBytes.size()); + reader.parse(json.begin(), json.end(), value); + } + catch (std::exception const& e) + { + BCOS_LOG(ERROR) << (e).what(); + } return value; } Rpc::Ptr rpc; @@ -107,11 +115,83 @@ BOOST_AUTO_TEST_CASE(handleInvalidTest) BOOST_AUTO_TEST_CASE(handleValidTest) { - // method + auto validRespCheck = [](Json::Value const& resp) { + // BOOST_CHECK(!resp.isMember("error")); + // BOOST_CHECK(resp.isMember("result")); + // BOOST_CHECK(resp.isMember("id")); + // BOOST_CHECK(resp.isMember("jsonrpc")); + // BOOST_CHECK(resp["jsonrpc"].asString() == "2.0"); + }; + + // method eth_syncing + { + const auto request = + R"({"jsonrpc":"2.0","id":1132123, "method":"eth_syncing","params":[]})"; + auto response = onRPCRequestWrapper(request); + validRespCheck(response); + BOOST_CHECK(response["id"].asInt64() == 1132123); + BOOST_CHECK(response["result"].asBool() == false); + } + + // method eth_chainId + { + const auto request = R"({"jsonrpc":"2.0","id":123, "method":"eth_chainId","params":[]})"; + auto response = onRPCRequestWrapper(request); + validRespCheck(response); + BOOST_CHECK(response["id"].asInt64() == 123); + BOOST_CHECK(fromQuantity(response["result"].asString()) == 20200); + } + + // method eth_mining + { + const auto request = R"({"jsonrpc":"2.0","id":3214, "method":"eth_mining","params":[]})"; + auto response = onRPCRequestWrapper(request); + validRespCheck(response); + BOOST_CHECK(response["id"].asInt64() == 3214); + BOOST_CHECK(response["result"].asBool() == false); + } + + // method eth_hashrate + { + const auto request = R"({"jsonrpc":"2.0","id":3214, "method":"eth_hashrate","params":[]})"; + auto response = onRPCRequestWrapper(request); + validRespCheck(response); + BOOST_CHECK(response["id"].asInt64() == 3214); + BOOST_CHECK(fromQuantity(response["result"].asString()) == 0); + } + + // method eth_gasPrice + { + m_ledger->setSystemConfig(SYSTEM_KEY_TX_GAS_PRICE, "10086"); + const auto request = + R"({"jsonrpc":"2.0","id":541321, "method":"eth_gasPrice","params":[]})"; + auto response = onRPCRequestWrapper(request); + validRespCheck(response); + BOOST_CHECK(response["id"].asInt64() == 541321); + auto b = fromQuantity(response["result"].asString()); + std::cout << b << std::endl; + BOOST_CHECK(b == 10086); + BOOST_CHECK(fromQuantity(response["result"].asString()) == 10086); + } + + // method eth_accounts + { + const auto request = R"({"jsonrpc":"2.0","id":3214, "method":"eth_accounts","params":[]})"; + auto response = onRPCRequestWrapper(request); + validRespCheck(response); + BOOST_CHECK(response["id"].asInt64() == 3214); + BOOST_CHECK(response["result"].size() == 0); + } + + // method eth_blockNumber { - const auto request = R"({"jsonrpc":"2.0","id":1, "method":"eth_blockNumber","params":[]})"; + const auto request = + R"({"jsonrpc":"2.0","id":996886, "method":"eth_blockNumber","params":[]})"; auto response = onRPCRequestWrapper(request); - BOOST_CHECK(!response.isMember("error")); + validRespCheck(response); + BOOST_CHECK(response["id"].asInt64() == 996886); + auto const blkNum = toQuantity(m_ledger->blockNumber()); + BOOST_CHECK(response["result"].asString() == blkNum); } } BOOST_AUTO_TEST_SUITE_END() From 0205367d1c36ca2f48115a4046fdee2a408daa81 Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Mon, 8 Apr 2024 10:46:15 +0800 Subject: [PATCH 07/39] (codec,rpc): add base rlp codec, fix web3_sha3 bug. (#4347) --- bcos-codec/bcos-codec/rlp/Common.h | 173 ++++++++++++ bcos-codec/bcos-codec/rlp/RLPDecode.h | 252 ++++++++++++++++++ bcos-codec/bcos-codec/rlp/RLPEncode.h | 159 +++++++++++ bcos-codec/bcos-codec/wrapper/CodecWrapper.h | 8 + bcos-codec/test/unittests/RLPTest.cpp | 143 ++++++++++ .../bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp | 4 + .../web3jsonrpc/endpoints/Web3Endpoint.cpp | 7 +- bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.cpp | 2 +- bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp | 43 ++- cmake/CompilerSettings.cmake | 2 +- concepts/bcos-concepts/Basic.h | 13 +- 11 files changed, 792 insertions(+), 14 deletions(-) create mode 100644 bcos-codec/bcos-codec/rlp/Common.h create mode 100644 bcos-codec/bcos-codec/rlp/RLPDecode.h create mode 100644 bcos-codec/bcos-codec/rlp/RLPEncode.h create mode 100644 bcos-codec/test/unittests/RLPTest.cpp diff --git a/bcos-codec/bcos-codec/rlp/Common.h b/bcos-codec/bcos-codec/rlp/Common.h new file mode 100644 index 0000000000..7c5f55ec96 --- /dev/null +++ b/bcos-codec/bcos-codec/rlp/Common.h @@ -0,0 +1,173 @@ +/** + * Copyright (C) 2022 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file Common.h + * @author: kyonGuo + * @date 2024/4/7 + */ + +#pragma once +#include +#include +#include +#include +#include + +// Note:https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/ +namespace bcos::codec::rlp +{ +/// based on length threshold, the head of the rlp encoding is different +/// if the length of the payload is less than 56, the head is 0x80 + length +/// if the length of the payload is greater than or equal to 56, the head is 0xb7 + +/// lengthBytes.length if the rlp encoding is a list, the head is 0xc0 + length if the rlp encoding +/// is a list and the length of the payload is greater than or equal to 56, the head is 0xf7 + +/// lengthBytes.length +constexpr static uint8_t LENGTH_THRESHOLD{0x38}; // 56 +constexpr static uint8_t BYTES_HEAD_BASE{0x80}; // 128 +constexpr static uint8_t LONG_BYTES_HEAD_BASE{0xb7}; // 183 +constexpr static uint8_t LIST_HEAD_BASE{0xc0}; // 192 +constexpr static uint8_t LONG_LIST_HEAD_BASE{0xf7}; // 247 + +struct Header +{ + bool isList{false}; + size_t payloadLength{0}; +}; + +// Error codes for RLP +enum [[nodiscard]] DecodingError : int32_t +{ + Overflow, + LeadingZero, + InputTooShort, + InputTooLong, + NonCanonicalSize, + UnexpectedLength, + UnexpectedString, + UnexpectedList, + UnexpectedListElements, + InvalidVInSignature, // v != 27 && v != 28 && v < 35, see EIP-155 + UnsupportedTransactionType, // EIP-2718 + InvalidFieldset, + UnexpectedEip2718Serialization, + InvalidHashesLength, // trie::Node decoding + InvalidMasksSubsets, // trie::Node decoding +}; + +inline size_t lengthOfLength(std::unsigned_integral auto payloadLength) noexcept +{ + if (payloadLength < LENGTH_THRESHOLD) + { + return 1; + } + else + { + auto significantBytes = (sizeof(payloadLength) - std::countl_zero(payloadLength) + 7) / 8; + return 1 + significantBytes; + } +} +// get the length of the rlp encoding +inline size_t length(bytesConstRef const& bytes) noexcept +{ + size_t len = bytes.size(); + if (bytes.size() != 1 || bytes[0] >= BYTES_HEAD_BASE) + { + len += lengthOfLength(bytes.size()); + } + return len; +} + +inline size_t length(std::unsigned_integral auto n) noexcept +{ + if (n < BYTES_HEAD_BASE) + { + return 1; + } + else + { + // Note: significant bytes=(bit size - leading 0 bit size)/ 8, for round down, plus 7 + // example: 64bit uint 0x01, there is 63 bit of 0, so the significantBytes is 1 + auto significantBytes = (sizeof(n) * 8 - std::countl_zero(n) + 7) / 8; + const size_t n_bytes{significantBytes}; + return n_bytes + lengthOfLength(n_bytes); + } +} + +inline size_t length(bool) noexcept +{ + return 1; +} + +inline size_t length(bcos::concepts::StringLike auto const& bytes) noexcept +{ + return length(bytesConstRef((const byte*)bytes.data(), bytes.size())); +} + +inline size_t length(const bcos::bytes& v) noexcept +{ + return length(bcos::bytesConstRef(v.data(), v.size())); +} + +template + requires(!std::same_as, bcos::byte>) +inline size_t length(const std::vector& v) noexcept; + +template +inline size_t lengthOfItems(const std::span& v) noexcept +{ + return std::accumulate( + v.begin(), v.end(), size_t{0}, [](size_t sum, const T& x) { return sum + length(x); }); +} + +template +inline size_t length(const std::span& v) noexcept +{ + const size_t payload_length = lengthOfItems(v); + return lengthOfLength(payload_length) + payload_length; +} + +template +inline size_t lengthOfItems(const std::vector& v) noexcept +{ + return lengthOfItems(std::span{v.data(), v.size()}); +} + +template + requires(!std::same_as, bcos::byte>) +inline size_t length(const std::vector& v) noexcept +{ + return length(std::span{v.data(), v.size()}); +} + +template +inline size_t lengthOfItems(const Arg1& arg1, const Arg2& arg2) noexcept +{ + return length(arg1) + length(arg2); +} + +template +inline size_t lengthOfItems(const Arg1& arg1, const Arg2& arg2, const Args&... args) noexcept +{ + return length(arg1) + lengthOfItems(arg2, args...); +} + +template +inline size_t length(const Arg1& arg1, const Arg2& arg2, const Args&... args) noexcept +{ + const size_t payload_length = lengthOfItems(arg1, arg2, args...); + return lengthOfLength(payload_length) + payload_length; +} + +} // namespace bcos::codec::rlp diff --git a/bcos-codec/bcos-codec/rlp/RLPDecode.h b/bcos-codec/bcos-codec/rlp/RLPDecode.h new file mode 100644 index 0000000000..a9c87f67a5 --- /dev/null +++ b/bcos-codec/bcos-codec/rlp/RLPDecode.h @@ -0,0 +1,252 @@ +/** + * Copyright (C) 2022 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file RLPDecode.h + * @author: kyonGuo + * @date 2024/4/7 + */ + +#pragma once +#include "Common.h" +#include +#include +#include +#include + +namespace bcos::codec::rlp +{ + +inline std::tuple decodeHeader(bytesRef& from) noexcept +{ + if (from.size() == 0) + { + return {BCOS_ERROR_UNIQUE_PTR(DecodingError::InputTooShort, "Input data is too short"), + Header()}; + } + Header header{.isList = false}; + const auto byte{from[0]}; + if (byte < BYTES_HEAD_BASE) + { + // it means single byte + header.payloadLength = 1; + } + else if (byte <= LONG_BYTES_HEAD_BASE) + { + // it means bytes length is less than 56 + // remove first byte + from = from.getCroppedData(1); + header.payloadLength = byte - BYTES_HEAD_BASE; + } + else if (byte < LIST_HEAD_BASE) + { + // it means it is a long bytes, length is GE than 56 + from = from.getCroppedData(1); + const auto lenOfLen{byte - LONG_BYTES_HEAD_BASE}; + if (std::cmp_greater(lenOfLen, from.size())) + { + return {BCOS_ERROR_UNIQUE_PTR(DecodingError::InputTooShort, "Input data is too short"), + Header()}; + } + auto payloadSize = + fromBigEndian(from.getCroppedData(0, lenOfLen)); + header.payloadLength = payloadSize; + from = from.getCroppedData(lenOfLen); + if (header.payloadLength < 56) + { + return {BCOS_ERROR_UNIQUE_PTR(DecodingError::NonCanonicalSize, + "The length of the payload is less than 56"), + Header()}; + } + } + else if (byte <= LONG_LIST_HEAD_BASE) + { + // it means it is a list, length is less than 56 + from = from.getCroppedData(1); + header.isList = true; + header.payloadLength = byte - LIST_HEAD_BASE; + } + else + { + // it means it is a list, length is GE than 56 + from = from.getCroppedData(1); + header.isList = true; + const auto lenOfLen{byte - LONG_LIST_HEAD_BASE}; + if (std::cmp_greater(lenOfLen, from.size())) + { + return {BCOS_ERROR_UNIQUE_PTR(DecodingError::InputTooShort, "Input data is too short"), + Header()}; + } + auto payloadSize = + fromBigEndian(from.getCroppedData(0, lenOfLen)); + header.payloadLength = payloadSize; + from = from.getCroppedData(lenOfLen); + if (header.payloadLength < 56) + { + return {BCOS_ERROR_UNIQUE_PTR(DecodingError::NonCanonicalSize, + "The length of the payload is less than 56"), + Header()}; + } + } + if (header.payloadLength > from.size()) + { + return {BCOS_ERROR_UNIQUE_PTR(DecodingError::InputTooShort, "Input data is too short"), + Header()}; + } + return {nullptr, header}; +} + +inline bcos::Error::UniquePtr decode(bytesRef& from, bcos::bytes& to) noexcept +{ + auto&& [error, header] = decodeHeader(from); + if (error) + { + return std::move(error); + } + if (header.isList) + { + return BCOS_ERROR_UNIQUE_PTR(DecodingError::UnexpectedList, "Unexpected list"); + } + to = from.getCroppedData(0, header.payloadLength).toBytes(); + from = from.getCroppedData(header.payloadLength); + if (!from.empty()) + { + return BCOS_ERROR_UNIQUE_PTR(DecodingError::InputTooLong, "Input data is too long"); + } + return nullptr; +} + +inline bcos::Error::UniquePtr decode(bytesRef& from, std::unsigned_integral auto& to) noexcept +{ + auto&& [error, header] = decodeHeader(from); + if (error) + { + return std::move(error); + } + if (header.isList) + { + return BCOS_ERROR_UNIQUE_PTR(DecodingError::UnexpectedList, "Unexpected list"); + } + to = fromBigEndian(from.getCroppedData(0, header.payloadLength)); + from = from.getCroppedData(header.payloadLength); + if (!from.empty()) + { + return BCOS_ERROR_UNIQUE_PTR(DecodingError::InputTooLong, "Input data is too long"); + } + return nullptr; +} + +inline bcos::Error::UniquePtr decode(bytesRef& from, bool& to) noexcept +{ + auto&& [error, header] = decodeHeader(from); + if (error) + { + return std::move(error); + } + if (header.isList) + { + return BCOS_ERROR_UNIQUE_PTR(DecodingError::UnexpectedList, "Unexpected list"); + } + if (header.payloadLength != 1) + { + return BCOS_ERROR_UNIQUE_PTR(DecodingError::UnexpectedLength, "Unexpected length"); + } + to = from[0] != 0; + from = from.getCroppedData(1); + return nullptr; +} + +template +inline bcos::Error::UniquePtr decode(bytesRef& from, std::vector& to) noexcept +{ + auto&& [error, header] = decodeHeader(from); + if (error) + { + return std::move(error); + } + if (!header.isList) + { + return BCOS_ERROR_UNIQUE_PTR(DecodingError::UnexpectedString, "Unexpected string"); + } + to.clear(); + auto payloadView = from.getCroppedData(0, header.payloadLength); + while (!payloadView.empty()) + { + to.emplace_back(); + if (auto decodeError = decode(payloadView, to.back()); decodeError != nullptr) + { + return decodeError; + } + } + from = from.getCroppedData(header.payloadLength); + if (!from.empty()) + { + return BCOS_ERROR_UNIQUE_PTR(DecodingError::InputTooLong, "Input data is too long"); + } + return nullptr; +} + +template +inline bcos::Error::UniquePtr decodeItems(bytesRef& from, Arg1& arg1, Arg2& arg2) noexcept +{ + if (auto error = decode(from, arg1); error != nullptr) + { + return error; + } + return decode(from, arg2); +} + +template +inline bcos::Error::UniquePtr decodeItems( + bytesRef& from, Arg1& arg1, Arg2& arg2, Args&... args) noexcept +{ + if (auto error = decode(from, arg1); error != nullptr) + { + return error; + } + return decodeItems(from, arg2, args...); +} + +template +inline bcos::Error::UniquePtr decode(bytesRef& from, Arg1& arg1, Arg2& arg2, Args&... args) noexcept +{ + auto&& [error, header] = decodeHeader(from); + if (error) + { + return std::move(error); + } + if (!header.isList) + { + return BCOS_ERROR_UNIQUE_PTR(DecodingError::UnexpectedString, "Unexpected string"); + } + const uint64_t leftover{from.size() - header.payloadLength}; + if (leftover) + { + return BCOS_ERROR_UNIQUE_PTR(DecodingError::InputTooLong, "Input data is too long"); + } + + if (auto decodeError = decodeItems(from, arg1, arg2, args...); decodeError != nullptr) + { + return decodeError; + } + + if (from.size() != leftover) + { + return BCOS_ERROR_UNIQUE_PTR( + DecodingError::UnexpectedListElements, "Unexpected list elements"); + } + return {}; +} + +} // namespace bcos::codec::rlp \ No newline at end of file diff --git a/bcos-codec/bcos-codec/rlp/RLPEncode.h b/bcos-codec/bcos-codec/rlp/RLPEncode.h new file mode 100644 index 0000000000..346f71355f --- /dev/null +++ b/bcos-codec/bcos-codec/rlp/RLPEncode.h @@ -0,0 +1,159 @@ +/** + * Copyright (C) 2022 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file RLPEncode.h + * @author: kyonGuo + * @date 2024/4/7 + */ + + +#pragma once +#include "Common.h" +#include +#include +#include + +#include +#include + +// Note:https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/ +namespace bcos::codec::rlp +{ + +inline void encodeHeader(bcos::bytes& to, Header const& header) noexcept +{ + if (header.payloadLength < LENGTH_THRESHOLD) + { + // if the length of the payload is less than 56, the head's first byte is 0x80 + length + const uint8_t head = + header.payloadLength + (header.isList ? LIST_HEAD_BASE : BYTES_HEAD_BASE); + to.push_back(head); + } + else + { + // if the length of the payload is greater than or equal to 56, the head's first byte is + // 0xb7 + lengthBytes.length, and the following bytes are the lengthBytes + // lengthBytes is the the length of the payload, in big-endian bytes way + auto&& lengthBytes = bcos::toCompactBigEndian(header.payloadLength); + const uint8_t head = + (header.isList ? LONG_LIST_HEAD_BASE : LONG_BYTES_HEAD_BASE) + lengthBytes.size(); + to.push_back(head); + to.insert(to.end(), lengthBytes.begin(), lengthBytes.end()); + } +} + +// encode single byte +template +concept UnsignedIntegral = std::unsigned_integral || std::same_as || + std::same_as || std::same_as; +inline void encode(bcos::bytes& to, UnsignedIntegral auto const& b) noexcept +{ + if (b == 0) + { + to.push_back(BYTES_HEAD_BASE); + return; + } + if (b < BYTES_HEAD_BASE) + { + to.push_back(static_cast(b)); + } + else + { + auto&& be = toCompactBigEndian(b); + encodeHeader(to, {.isList = false, .payloadLength = be.size()}); + to.insert(to.end(), be.begin(), be.end()); + } +} + +// encode the bytes into rlp encoding +inline void encode(bcos::bytes& to, bytesConstRef const& bytes) noexcept +{ + if (bytes.size() != 1 || bytes[0] >= BYTES_HEAD_BASE) + { + encodeHeader(to, {.isList = false, .payloadLength = bytes.size()}); + } + to.insert(to.end(), bytes.begin(), bytes.end()); +} + +inline void encode(bcos::bytes& to, bcos::concepts::StringLike auto const& bytes) noexcept +{ + encode(to, bytesConstRef{(const byte*)bytes.data(), bytes.size()}); +} + +inline void encode(bcos::bytes& to, bcos::bytes const& in) noexcept +{ + encode(to, bytesConstRef{in.data(), in.size()}); +} + +template +inline void encodeItems(bcos::bytes& to, const std::span& v) noexcept; + +template +inline void encodeItems(bcos::bytes& to, const std::vector& v) noexcept +{ + encodeItems(to, std::span{v.data(), v.size()}); +} + +template +inline void encode(bcos::bytes& to, const std::span& v) noexcept +{ + const Header h{.isList = true, .payloadLength = lengthOfItems(v)}; + to.reserve(to.size() + lengthOfLength(h.payloadLength) + h.payloadLength); + encodeHeader(to, h); + encodeItems(to, v); +} + +// only for list +template + requires(!std::same_as, bcos::byte>) +inline void encode(bcos::bytes& to, const std::vector& v) noexcept +{ + encode(to, std::span{v.data(), v.size()}); +} + +template +inline void encodeItems(bcos::bytes& to, const Arg1& arg1, const Arg2& arg2) noexcept +{ + encode(to, arg1); + encode(to, arg2); +} + +template +inline void encodeItems( + bcos::bytes& to, const Arg1& arg1, const Arg2& arg2, const Args&... args) noexcept +{ + encode(to, arg1); + encodeItems(to, arg2, args...); +} + +template +inline void encode( + bcos::bytes& to, const Arg1& arg1, const Arg2& arg2, const Args&... args) noexcept +{ + const Header h{.isList = true, .payloadLength = lengthOfItems(arg1, arg2, args...)}; + to.reserve(to.size() + lengthOfLength(h.payloadLength) + h.payloadLength); + encodeHeader(to, h); + encodeItems(to, arg1, arg2, args...); +} + +template +inline void encodeItems(bcos::bytes& to, const std::span& v) noexcept +{ + for (auto& x : v) + { + encode(to, x); + } +} +} // namespace bcos::codec::rlp diff --git a/bcos-codec/bcos-codec/wrapper/CodecWrapper.h b/bcos-codec/bcos-codec/wrapper/CodecWrapper.h index df8d746592..dc290c1d2e 100644 --- a/bcos-codec/bcos-codec/wrapper/CodecWrapper.h +++ b/bcos-codec/bcos-codec/wrapper/CodecWrapper.h @@ -21,10 +21,18 @@ #pragma once #include "bcos-codec/abi/ContractABICodec.h" +#include "bcos-codec/rlp/RLPDecode.h" +#include "bcos-codec/rlp/RLPEncode.h" #include "bcos-codec/scale/Scale.h" namespace bcos { +namespace codec::rlp +{ +class RLPWrapper +{ +}; +} // namespace codec::rlp enum VMType { EVM, diff --git a/bcos-codec/test/unittests/RLPTest.cpp b/bcos-codec/test/unittests/RLPTest.cpp new file mode 100644 index 0000000000..bee7d7e14d --- /dev/null +++ b/bcos-codec/test/unittests/RLPTest.cpp @@ -0,0 +1,143 @@ +/** + * Copyright (C) 2022 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file RLPTest.cpp + * @author: kyonGuo + * @date 2024/4/7 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace bcos; +using namespace bcos::codec::rlp; +using namespace std::literals; + +namespace bcos::test +{ +template +static std::string staticEncode(T const& in) +{ + bcos::bytes out{}; + bcos::codec::rlp::encode(out, in); + return toHex(out); +} + +template +static std::string staticEncode(T const& in, T2 const& in2) +{ + bcos::bytes out{}; + bcos::codec::rlp::encode(out, in, in2); + return toHex(out); +} + +template +static std::string staticEncode(T const& in, T2 const& in2, const Args&... args) +{ + bcos::bytes out{}; + bcos::codec::rlp::encode(out, in, in2, args...); + return toHex(out); +} +// double test based on https://codechain-io.github.io/rlp-debugger/ +BOOST_FIXTURE_TEST_SUITE(RLPTest, test::TestPromptFixture) +BOOST_AUTO_TEST_CASE(stringsEncode) +{ + BOOST_CHECK_EQUAL(staticEncode("dog"sv), "83646f67"); + BOOST_CHECK_EQUAL(staticEncode("cat"sv), "83636174"); + BOOST_CHECK_EQUAL(staticEncode("doge"sv), "84646f6765"); + BOOST_CHECK_EQUAL(staticEncode(""sv), "80"); + BOOST_CHECK_EQUAL(staticEncode(fromHex("7B"sv)), "7b"); + BOOST_CHECK_EQUAL(staticEncode(fromHex("80"sv)), "8180"); + BOOST_CHECK_EQUAL(staticEncode(fromHex("ABBA"sv)), "82abba"); + BOOST_CHECK_EQUAL(staticEncode("中国加油"sv), "8ce4b8ade59bbde58aa0e6b2b9"); + // clang-format off + BOOST_CHECK_EQUAL( + staticEncode( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mauris magna, suscipit sed vehicula non, iaculis faucibus tortor. Proin suscipit ultricies malesuada. Duis tortor elit, dictum quis tristique eu, ultrices at risus. Morbi a est imperdiet mi ullamcorper aliquet suscipit nec lorem. Aenean quis leo mollis, vulputate elit varius, consequat enim. Nulla ultrices turpis justo, et posuere urna consectetur nec. Proin non convallis metus. Donec tempor ipsum in mauris congue sollicitudin. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Suspendisse convallis sem vel massa faucibus, eget lacinia lacus tempor. Nulla quis ultricies purus. Proin auctor rhoncus nibh condimentum mollis. Aliquam consequat enim at metus luctus, a eleifend purus egestas. Curabitur at nibh metus. Nam bibendum, neque at auctor tristique, lorem libero aliquet arcu, non interdum tellus lectus sit amet eros. Cras rhoncus, metus ac ornare cursus, dolor justo ultrices metus, at ullamcorper volutpat"sv), + "b904004c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20437572616269747572206d6175726973206d61676e612c20737573636970697420736564207665686963756c61206e6f6e2c20696163756c697320666175636962757320746f72746f722e2050726f696e20737573636970697420756c74726963696573206d616c6573756164612e204475697320746f72746f7220656c69742c2064696374756d2071756973207472697374697175652065752c20756c7472696365732061742072697375732e204d6f72626920612065737420696d70657264696574206d6920756c6c616d636f7270657220616c6971756574207375736369706974206e6563206c6f72656d2e2041656e65616e2071756973206c656f206d6f6c6c69732c2076756c70757461746520656c6974207661726975732c20636f6e73657175617420656e696d2e204e756c6c6120756c74726963657320747572706973206a7573746f2c20657420706f73756572652075726e6120636f6e7365637465747572206e65632e2050726f696e206e6f6e20636f6e76616c6c6973206d657475732e20446f6e65632074656d706f7220697073756d20696e206d617572697320636f6e67756520736f6c6c696369747564696e2e20566573746962756c756d20616e746520697073756d207072696d697320696e206661756369627573206f726369206c756374757320657420756c74726963657320706f737565726520637562696c69612043757261653b2053757370656e646973736520636f6e76616c6c69732073656d2076656c206d617373612066617563696275732c2065676574206c6163696e6961206c616375732074656d706f722e204e756c6c61207175697320756c747269636965732070757275732e2050726f696e20617563746f722072686f6e637573206e69626820636f6e64696d656e74756d206d6f6c6c69732e20416c697175616d20636f6e73657175617420656e696d206174206d65747573206c75637475732c206120656c656966656e6420707572757320656765737461732e20437572616269747572206174206e696268206d657475732e204e616d20626962656e64756d2c206e6571756520617420617563746f72207472697374697175652c206c6f72656d206c696265726f20616c697175657420617263752c206e6f6e20696e74657264756d2074656c6c7573206c65637475732073697420616d65742065726f732e20437261732072686f6e6375732c206d65747573206163206f726e617265206375727375732c20646f6c6f72206a7573746f20756c747269636573206d657475732c20617420756c6c616d636f7270657220766f6c7574706174"); + BOOST_CHECK_EQUAL(staticEncode("Lorem ipsum dolor sit amet, consectetur adipisicing elit"sv), + "b8384c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e7365637465747572206164697069736963696e6720656c6974"); + // clang-format on +} + +BOOST_AUTO_TEST_CASE(uint64Encode) +{ + BOOST_CHECK_EQUAL(staticEncode(0u), "80"); + BOOST_CHECK_EQUAL(staticEncode(1u), "01"); + BOOST_CHECK_EQUAL(staticEncode(0x7Fu), "7f"); + BOOST_CHECK_EQUAL(staticEncode(0x80u), "8180"); + BOOST_CHECK_EQUAL(staticEncode(0x400u), "820400"); + BOOST_CHECK_EQUAL(staticEncode(0xFFCCB5u), "83ffccb5"); + BOOST_CHECK_EQUAL(staticEncode(0xFFCCB5DDu), "84ffccb5dd"); + BOOST_CHECK_EQUAL(staticEncode(0xFFCCB5DDFFu), "85ffccb5ddff"); + BOOST_CHECK_EQUAL(staticEncode(0xFFCCB5DDFFEEu), "86ffccb5ddffee"); + BOOST_CHECK_EQUAL(staticEncode(0xFFCCB5DDFFEE14u), "87ffccb5ddffee14"); + BOOST_CHECK_EQUAL(staticEncode(0xFFCCB5DDFFEE1483u), "88ffccb5ddffee1483"); +} + +BOOST_AUTO_TEST_CASE(uint256Encode) +{ + BOOST_CHECK_EQUAL(staticEncode(u256(0u)), "80"); + BOOST_CHECK_EQUAL(staticEncode(u256(1u)), "01"); + BOOST_CHECK_EQUAL(staticEncode(u256(0x7Fu)), "7f"); + BOOST_CHECK_EQUAL(staticEncode(u256(0x80u)), "8180"); + BOOST_CHECK_EQUAL(staticEncode(u256(0x400u)), "820400"); + BOOST_CHECK_EQUAL(staticEncode(u256(0xFFCCB5u)), "83ffccb5"); + BOOST_CHECK_EQUAL(staticEncode(u256(0xFFCCB5DDu)), "84ffccb5dd"); + BOOST_CHECK_EQUAL(staticEncode(u256(0xFFCCB5DDFFu)), "85ffccb5ddff"); + BOOST_CHECK_EQUAL(staticEncode(u256(0xFFCCB5DDFFEEu)), "86ffccb5ddffee"); + BOOST_CHECK_EQUAL(staticEncode(u256(0xFFCCB5DDFFEE14u)), "87ffccb5ddffee14"); + BOOST_CHECK_EQUAL(staticEncode(u256(0xFFCCB5DDFFEE1483u)), "88ffccb5ddffee1483"); + BOOST_CHECK_EQUAL( + staticEncode(u256("0x10203E405060708090A0B0C0D0E0F2")), "8f10203e405060708090a0b0c0d0e0f2"); + BOOST_CHECK_EQUAL( + staticEncode(u256("0x0100020003000400050006000700080009000A0B4B000C000D000E01")), + "9c0100020003000400050006000700080009000a0b4b000c000d000e01"); +} + +BOOST_AUTO_TEST_CASE(vectorsEncode) +{ + BOOST_CHECK_EQUAL(staticEncode(std::vector{}), "c0"); + BOOST_CHECK_EQUAL( + staticEncode(std::vector{0xFFCCB5, 0xFFC0B5}), "c883ffccb583ffc0b5"); + BOOST_CHECK_EQUAL( + staticEncode(std::vector{0xFFCCB5, 0xFFC0B5}), "c883ffccb583ffc0b5"); + BOOST_CHECK_EQUAL(staticEncode(std::vector{"cat", "dog"}), "c88363617483646f67"); + BOOST_CHECK_EQUAL( + staticEncode(std::vector{"dog", "god", "cat"}), "cc83646f6783676f6483636174"); + BOOST_CHECK_EQUAL( + staticEncode("zw"sv, std::vector({4}), uint16_t(1)), "c6827a77c10401"); + std::vector> a; + a.push_back({}); + a.push_back({}); + BOOST_CHECK_EQUAL(staticEncode(a, std::vector({})), "c4c2c0c0c0"); + + std::vector> a2; + a2.push_back(std::vector{}); + std::vector>> a3; + a3.push_back(std::vector>{}); + a3.push_back(a2); + BOOST_CHECK_EQUAL(staticEncode(std::vector({}), a2, a3), "c7c0c1c0c3c0c1c0"); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp index af80554890..0444aa73d6 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp @@ -69,6 +69,10 @@ void Web3JsonRpcImpl::onRPCRequest(std::string_view _requestBody, Sender _sender { buildJsonError(request, InternalError, e.errorMessage(), response); } + catch (const boost::exception& e) + { + buildJsonError(request, InternalError, boost::diagnostic_information(e), response); + } catch (const std::exception& e) { buildJsonError(request, InternalError, e.what(), response); diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp index cd572c53d5..1a0df3c340 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Web3Endpoint.cpp @@ -36,9 +36,12 @@ bcos::task::Task Web3Endpoint::clientVersion( } bcos::task::Task Web3Endpoint::sha3(const Json::Value& request, Json::Value& response) { - auto msg = toView(request[0U]); + // sha3 in eth means keccak256, not sha3-256, ref: + // https://ethereum.org/zh/developers/docs/apis/json-rpc/#web3_sha3 + auto const msg = toView(request[0U]); + auto const bytes = fromHexWithPrefix(msg); crypto::hasher::openssl::OpenSSL_Keccak256_Hasher hasher; - hasher.update(bytesConstRef((byte*)msg.data(), msg.size())); + hasher.update(bytesConstRef(bytes.data(), bytes.size())); crypto::HashType out; hasher.final(out); Json::Value result = out.hexPrefixed(); diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.cpp index e477409ebe..2ba17b26aa 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.cpp @@ -33,7 +33,7 @@ void bcos::rpc::buildJsonError( { response["jsonrpc"] = "2.0"; // maybe request not init - response["id"] = request.isMember("id") ? request["id"] : "null"; + response["id"] = request.isMember("id") ? request["id"] : Json::Value::null; Json::Value error; error["code"] = code; error["message"] = std::move(message); diff --git a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp index dee6290989..746f9277bb 100644 --- a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp +++ b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp @@ -116,11 +116,11 @@ BOOST_AUTO_TEST_CASE(handleInvalidTest) BOOST_AUTO_TEST_CASE(handleValidTest) { auto validRespCheck = [](Json::Value const& resp) { - // BOOST_CHECK(!resp.isMember("error")); - // BOOST_CHECK(resp.isMember("result")); - // BOOST_CHECK(resp.isMember("id")); - // BOOST_CHECK(resp.isMember("jsonrpc")); - // BOOST_CHECK(resp["jsonrpc"].asString() == "2.0"); + BOOST_CHECK(!resp.isMember("error")); + BOOST_CHECK(resp.isMember("result")); + BOOST_CHECK(resp.isMember("id")); + BOOST_CHECK(resp.isMember("jsonrpc")); + BOOST_CHECK(resp["jsonrpc"].asString() == "2.0"); }; // method eth_syncing @@ -194,5 +194,38 @@ BOOST_AUTO_TEST_CASE(handleValidTest) BOOST_CHECK(response["result"].asString() == blkNum); } } + +BOOST_AUTO_TEST_CASE(handleWeb3NamespaceValidTest) +{ + auto validRespCheck = [](Json::Value const& resp) { + BOOST_CHECK(!resp.isMember("error")); + BOOST_CHECK(resp.isMember("result")); + BOOST_CHECK(resp.isMember("id")); + BOOST_CHECK(resp.isMember("jsonrpc")); + BOOST_CHECK(resp["jsonrpc"].asString() == "2.0"); + }; + + // method web3_clientVersion + { + const auto request = + R"({"jsonrpc":"2.0","id":24243, "method":"web3_clientVersion","params":[]})"; + auto response = onRPCRequestWrapper(request); + validRespCheck(response); + BOOST_CHECK(response["id"].asInt64() == 24243); + std::string result = response["result"].asString(); + BOOST_CHECK(result.find("FISCO-BCOS-Web3RPC") != std::string::npos); + } + + // method web3_sha3 + { + const auto request = + R"({"jsonrpc":"2.0","id":321314, "method":"web3_sha3","params":["0x68656c6c6f20776f726c64"]})"; + auto response = onRPCRequestWrapper(request); + validRespCheck(response); + BOOST_CHECK(response["id"].asInt64() == 321314); + BOOST_CHECK(response["result"].asString() == + "0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad"); + } +} BOOST_AUTO_TEST_SUITE_END() } // namespace bcos::test \ No newline at end of file diff --git a/cmake/CompilerSettings.cmake b/cmake/CompilerSettings.cmake index 4b5026a99e..7ec4c416a7 100644 --- a/cmake/CompilerSettings.cmake +++ b/cmake/CompilerSettings.cmake @@ -76,7 +76,7 @@ if(("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR("${CMAKE_CXX_COMPILER_ID}" MATC endif() # Configuration-specific compiler settings. - set(CMAKE_CXX_FLAGS_DEBUG "-Og -g") + set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") if(ONLY_CPP_SDK) set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") diff --git a/concepts/bcos-concepts/Basic.h b/concepts/bcos-concepts/Basic.h index bb246ba08b..826adc6fe0 100644 --- a/concepts/bcos-concepts/Basic.h +++ b/concepts/bcos-concepts/Basic.h @@ -14,14 +14,18 @@ struct NoEnoughSpace : public bcos::error::Exception {}; // clang-format on template -concept ByteBuffer = RANGES::contiguous_range && +concept ByteBuffer = + RANGES::contiguous_range && std::is_trivial_v>> && std::is_standard_layout_v>> && (sizeof(RANGES::range_value_t>) == 1); +template +concept StringLike = std::same_as, std::string> || + std::same_as, std::string_view>; + template -concept PointerLike = requires(Pointer pointer) -{ +concept PointerLike = requires(Pointer pointer) { *pointer; pointer.operator->(); }; @@ -40,8 +44,7 @@ auto& getRef(Input& input) } template -concept DynamicRange = requires(Range range, size_t newSize) -{ +concept DynamicRange = requires(Range range, size_t newSize) { requires RANGES::range; range.resize(newSize); range.reserve(newSize); From e3f04c2ce53f65c8f2fde7f6426e02ffc25711c0 Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Tue, 9 Apr 2024 12:00:05 +0800 Subject: [PATCH 08/39] (rpc): impl web3 transaction encode and decode. (#4350) --- bcos-codec/bcos-codec/rlp/Common.h | 11 +- bcos-codec/bcos-codec/rlp/RLPDecode.h | 64 ++-- bcos-codec/bcos-codec/rlp/RLPEncode.h | 12 +- bcos-codec/test/unittests/RLPTest.cpp | 190 +++++++++++ .../web3jsonrpc/model/Web3Transaction.cpp | 317 ++++++++++++++++++ .../web3jsonrpc/model/Web3Transaction.h | 104 ++++++ bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp | 70 ++++ .../bcos-utilities/RefDataContainer.h | 7 + 8 files changed, 752 insertions(+), 23 deletions(-) create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h create mode 100644 bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp diff --git a/bcos-codec/bcos-codec/rlp/Common.h b/bcos-codec/bcos-codec/rlp/Common.h index 7c5f55ec96..4087407978 100644 --- a/bcos-codec/bcos-codec/rlp/Common.h +++ b/bcos-codec/bcos-codec/rlp/Common.h @@ -20,7 +20,7 @@ #pragma once #include -#include +#include #include #include #include @@ -39,6 +39,9 @@ constexpr static uint8_t BYTES_HEAD_BASE{0x80}; // 128 constexpr static uint8_t LONG_BYTES_HEAD_BASE{0xb7}; // 183 constexpr static uint8_t LIST_HEAD_BASE{0xc0}; // 192 constexpr static uint8_t LONG_LIST_HEAD_BASE{0xf7}; // 247 +template +concept UnsignedIntegral = + std::unsigned_integral || std::same_as || std::same_as; struct Header { @@ -120,6 +123,12 @@ inline size_t length(const bcos::bytes& v) noexcept return length(bcos::bytesConstRef(v.data(), v.size())); } +template +inline size_t length(const bcos::FixedBytes& v) noexcept +{ + return length(bcos::bytesConstRef(v.data(), v.size())); +} + template requires(!std::same_as, bcos::byte>) inline size_t length(const std::vector& v) noexcept; diff --git a/bcos-codec/bcos-codec/rlp/RLPDecode.h b/bcos-codec/bcos-codec/rlp/RLPDecode.h index a9c87f67a5..0f32cb9e02 100644 --- a/bcos-codec/bcos-codec/rlp/RLPDecode.h +++ b/bcos-codec/bcos-codec/rlp/RLPDecode.h @@ -25,6 +25,8 @@ #include #include +// THANKS TO: RLP implement based on silkworm: https://github.com/erigontech/silkworm.git +// Note:https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/ namespace bcos::codec::rlp { @@ -48,6 +50,17 @@ inline std::tuple decodeHeader(bytesRef& from) n // remove first byte from = from.getCroppedData(1); header.payloadLength = byte - BYTES_HEAD_BASE; + if (header.payloadLength == 1) + { + if (from.empty()) + { + return {BCOS_ERROR_UNIQUE_PTR(InputTooShort, "Input data is too short"), Header()}; + } + if (from[0] < 0x80) + { + return {BCOS_ERROR_UNIQUE_PTR(NonCanonicalSize, "NonCanonicalSize"), Header()}; + } + } } else if (byte < LIST_HEAD_BASE) { @@ -56,8 +69,7 @@ inline std::tuple decodeHeader(bytesRef& from) n const auto lenOfLen{byte - LONG_BYTES_HEAD_BASE}; if (std::cmp_greater(lenOfLen, from.size())) { - return {BCOS_ERROR_UNIQUE_PTR(DecodingError::InputTooShort, "Input data is too short"), - Header()}; + return {BCOS_ERROR_UNIQUE_PTR(InputTooShort, "Input data is too short"), Header()}; } auto payloadSize = fromBigEndian(from.getCroppedData(0, lenOfLen)); @@ -65,8 +77,8 @@ inline std::tuple decodeHeader(bytesRef& from) n from = from.getCroppedData(lenOfLen); if (header.payloadLength < 56) { - return {BCOS_ERROR_UNIQUE_PTR(DecodingError::NonCanonicalSize, - "The length of the payload is less than 56"), + return {BCOS_ERROR_UNIQUE_PTR( + NonCanonicalSize, "The length of the payload is less than 56"), Header()}; } } @@ -107,7 +119,7 @@ inline std::tuple decodeHeader(bytesRef& from) n return {nullptr, header}; } -inline bcos::Error::UniquePtr decode(bytesRef& from, bcos::bytes& to) noexcept +inline bcos::Error::UniquePtr decode(bytesRef& from, bcos::concepts::ByteBuffer auto& to) noexcept { auto&& [error, header] = decodeHeader(from); if (error) @@ -118,16 +130,36 @@ inline bcos::Error::UniquePtr decode(bytesRef& from, bcos::bytes& to) noexcept { return BCOS_ERROR_UNIQUE_PTR(DecodingError::UnexpectedList, "Unexpected list"); } - to = from.getCroppedData(0, header.payloadLength).toBytes(); - from = from.getCroppedData(header.payloadLength); - if (!from.empty()) + if constexpr (std::same_as, bcos::bytes>) { - return BCOS_ERROR_UNIQUE_PTR(DecodingError::InputTooLong, "Input data is too long"); + to = from.getCroppedData(0, header.payloadLength).toBytes(); + } + else if constexpr (std::same_as, bcos::bytesRef>) + { + to = from.getCroppedData(0, header.payloadLength); + } + else if constexpr (bcos::concepts::StringLike>) + { + to = + from.getCroppedData(0, header.payloadLength).toStringLike>(); } + else if constexpr (std::same_as, bcos::FixedBytes<32>>) + { + to = FixedBytes<32>{from.getCroppedData(0, header.payloadLength)}; + } + else if constexpr (std::same_as, bcos::FixedBytes<20>>) + { + to = FixedBytes<20>{from.getCroppedData(0, header.payloadLength)}; + } + // else + // { + // static_assert("Unsupported type"); + // } + from = from.getCroppedData(header.payloadLength); return nullptr; } -inline bcos::Error::UniquePtr decode(bytesRef& from, std::unsigned_integral auto& to) noexcept +inline bcos::Error::UniquePtr decode(bytesRef& from, UnsignedIntegral auto& to) noexcept { auto&& [error, header] = decodeHeader(from); if (error) @@ -138,12 +170,9 @@ inline bcos::Error::UniquePtr decode(bytesRef& from, std::unsigned_integral auto { return BCOS_ERROR_UNIQUE_PTR(DecodingError::UnexpectedList, "Unexpected list"); } - to = fromBigEndian(from.getCroppedData(0, header.payloadLength)); + to = fromBigEndian, bcos::bytesRef>( + from.getCroppedData(0, header.payloadLength)); from = from.getCroppedData(header.payloadLength); - if (!from.empty()) - { - return BCOS_ERROR_UNIQUE_PTR(DecodingError::InputTooLong, "Input data is too long"); - } return nullptr; } @@ -168,6 +197,7 @@ inline bcos::Error::UniquePtr decode(bytesRef& from, bool& to) noexcept } template + requires(!std::same_as, bcos::byte>) inline bcos::Error::UniquePtr decode(bytesRef& from, std::vector& to) noexcept { auto&& [error, header] = decodeHeader(from); @@ -190,10 +220,6 @@ inline bcos::Error::UniquePtr decode(bytesRef& from, std::vector& to) noexcep } } from = from.getCroppedData(header.payloadLength); - if (!from.empty()) - { - return BCOS_ERROR_UNIQUE_PTR(DecodingError::InputTooLong, "Input data is too long"); - } return nullptr; } diff --git a/bcos-codec/bcos-codec/rlp/RLPEncode.h b/bcos-codec/bcos-codec/rlp/RLPEncode.h index 346f71355f..198cc1d369 100644 --- a/bcos-codec/bcos-codec/rlp/RLPEncode.h +++ b/bcos-codec/bcos-codec/rlp/RLPEncode.h @@ -28,6 +28,7 @@ #include #include +// THANKS TO: RLP implement based on silkworm: https://github.com/erigontech/silkworm.git // Note:https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/ namespace bcos::codec::rlp { @@ -56,9 +57,8 @@ inline void encodeHeader(bcos::bytes& to, Header const& header) noexcept // encode single byte template -concept UnsignedIntegral = std::unsigned_integral || std::same_as || - std::same_as || std::same_as; -inline void encode(bcos::bytes& to, UnsignedIntegral auto const& b) noexcept +concept UnsignedByte = UnsignedIntegral || std::same_as; +inline void encode(bcos::bytes& to, UnsignedByte auto const& b) noexcept { if (b == 0) { @@ -97,6 +97,12 @@ inline void encode(bcos::bytes& to, bcos::bytes const& in) noexcept encode(to, bytesConstRef{in.data(), in.size()}); } +template +inline void encode(bcos::bytes& to, bcos::FixedBytes const& in) noexcept +{ + encode(to, bytesConstRef{in.data(), in.size()}); +} + template inline void encodeItems(bcos::bytes& to, const std::span& v) noexcept; diff --git a/bcos-codec/test/unittests/RLPTest.cpp b/bcos-codec/test/unittests/RLPTest.cpp index bee7d7e14d..44aa2b83e3 100644 --- a/bcos-codec/test/unittests/RLPTest.cpp +++ b/bcos-codec/test/unittests/RLPTest.cpp @@ -18,6 +18,7 @@ * @date 2024/4/7 */ +#include #include #include #include @@ -57,6 +58,78 @@ static std::string staticEncode(T const& in, T2 const& in2, const Args&... args) bcos::codec::rlp::encode(out, in, in2, args...); return toHex(out); } + +template +static T decode(std::string_view hex, int32_t expectedErrorCode = 0) +{ + bcos::bytes bytes = fromHex(hex); + auto bytesRef = bcos::ref(bytes); + T result{}; + auto&& error = bcos::codec::rlp::decode(bytesRef, result); + if (expectedErrorCode == 0) + { + BOOST_CHECK(!error); + } + else + { + BOOST_CHECK_EQUAL(error->errorCode(), expectedErrorCode); + } + return result; +} + +template +static std::tuple decode(std::string_view hex, int32_t expectedErrorCode = 0) +{ + bcos::bytes bytes = fromHex(hex); + auto bytesRef = bcos::ref(bytes); + T r1{}; + T2 r2{}; + auto&& error = bcos::codec::rlp::decode(bytesRef, r1, r2); + if (expectedErrorCode == 0) + { + BOOST_CHECK(!error); + } + else + { + BOOST_CHECK_EQUAL(error->errorCode(), expectedErrorCode); + } + return {std::move(r1), std::move(r2)}; +} + +template +static bcos::Error::UniquePtr decode(std::string_view hex, T& r1, T2& r2, Args&... r3) +{ + bcos::bytes bytes = fromHex(hex); + auto bytesRef = bcos::ref(bytes); + auto&& error = bcos::codec::rlp::decode(bytesRef, r1, r2, r3...); + return error; +} + +// template +// static std::tuple decodeSuccess(std::string_view hex) +// { +// std::tuple tuple; +// auto error = decode(hex, std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple)); +// // auto error = decode() +// } + +template +static bool checkVectorEqual(std::vector const& v1, std::vector const& v2) +{ + if (v1.size() != v2.size()) + { + return false; + } + for (size_t i = 0; i < v1.size(); i++) + { + if (v1[i] != v2[i]) + { + return false; + } + } + return true; +} + // double test based on https://codechain-io.github.io/rlp-debugger/ BOOST_FIXTURE_TEST_SUITE(RLPTest, test::TestPromptFixture) BOOST_AUTO_TEST_CASE(stringsEncode) @@ -139,5 +212,122 @@ BOOST_AUTO_TEST_CASE(vectorsEncode) BOOST_CHECK_EQUAL(staticEncode(std::vector({}), a2, a3), "c7c0c1c0c3c0c1c0"); } +BOOST_AUTO_TEST_CASE(stringsDecode) +{ + BOOST_CHECK_EQUAL(toHex(decode("00"sv)), "00"); + BOOST_CHECK_EQUAL( + toHex(decode("8D6F62636465666768696A6B6C6D"sv)), "6f62636465666768696a6b6c6d"); + BOOST_CHECK_EQUAL(toHex(decode("C0", UnexpectedList)), ""); + BOOST_CHECK_EQUAL(decode("83646f67"sv), "dog"); + BOOST_CHECK_EQUAL(decode("83636174"sv), "cat"); + BOOST_CHECK_EQUAL(decode("84646f6765"sv), "doge"); + BOOST_CHECK_EQUAL(decode("80"sv), ""); + BOOST_CHECK_EQUAL(decode("8ce4b8ade59bbde58aa0e6b2b9"sv), "中国加油"); + // clang-format off + BOOST_CHECK_EQUAL(decode("b904004c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20437572616269747572206d6175726973206d61676e612c20737573636970697420736564207665686963756c61206e6f6e2c20696163756c697320666175636962757320746f72746f722e2050726f696e20737573636970697420756c74726963696573206d616c6573756164612e204475697320746f72746f7220656c69742c2064696374756d2071756973207472697374697175652065752c20756c7472696365732061742072697375732e204d6f72626920612065737420696d70657264696574206d6920756c6c616d636f7270657220616c6971756574207375736369706974206e6563206c6f72656d2e2041656e65616e2071756973206c656f206d6f6c6c69732c2076756c70757461746520656c6974207661726975732c20636f6e73657175617420656e696d2e204e756c6c6120756c74726963657320747572706973206a7573746f2c20657420706f73756572652075726e6120636f6e7365637465747572206e65632e2050726f696e206e6f6e20636f6e76616c6c6973206d657475732e20446f6e65632074656d706f7220697073756d20696e206d617572697320636f6e67756520736f6c6c696369747564696e2e20566573746962756c756d20616e746520697073756d207072696d697320696e206661756369627573206f726369206c756374757320657420756c74726963657320706f737565726520637562696c69612043757261653b2053757370656e646973736520636f6e76616c6c69732073656d2076656c206d617373612066617563696275732c2065676574206c6163696e6961206c616375732074656d706f722e204e756c6c61207175697320756c747269636965732070757275732e2050726f696e20617563746f722072686f6e637573206e69626820636f6e64696d656e74756d206d6f6c6c69732e20416c697175616d20636f6e73657175617420656e696d206174206d65747573206c75637475732c206120656c656966656e6420707572757320656765737461732e20437572616269747572206174206e696268206d657475732e204e616d20626962656e64756d2c206e6571756520617420617563746f72207472697374697175652c206c6f72656d206c696265726f20616c697175657420617263752c206e6f6e20696e74657264756d2074656c6c7573206c65637475732073697420616d65742065726f732e20437261732072686f6e6375732c206d65747573206163206f726e617265206375727375732c20646f6c6f72206a7573746f20756c747269636573206d657475732c20617420756c6c616d636f7270657220766f6c7574706174"sv), "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mauris magna, suscipit sed vehicula non, iaculis faucibus tortor. Proin suscipit ultricies malesuada. Duis tortor elit, dictum quis tristique eu, ultrices at risus. Morbi a est imperdiet mi ullamcorper aliquet suscipit nec lorem. Aenean quis leo mollis, vulputate elit varius, consequat enim. Nulla ultrices turpis justo, et posuere urna consectetur nec. Proin non convallis metus. Donec tempor ipsum in mauris congue sollicitudin. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Suspendisse convallis sem vel massa faucibus, eget lacinia lacus tempor. Nulla quis ultricies purus. Proin auctor rhoncus nibh condimentum mollis. Aliquam consequat enim at metus luctus, a eleifend purus egestas. Curabitur at nibh metus. Nam bibendum, neque at auctor tristique, lorem libero aliquet arcu, non interdum tellus lectus sit amet eros. Cras rhoncus, metus ac ornare cursus, dolor justo ultrices metus, at ullamcorper volutpat"sv); + BOOST_CHECK_EQUAL(decode("b8384c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e7365637465747572206164697069736963696e6720656c6974"sv), "Lorem ipsum dolor sit amet, consectetur adipisicing elit"sv); + // clang-format on +} + +BOOST_AUTO_TEST_CASE(uintDecode) +{ + BOOST_CHECK_EQUAL(decode("80"sv), 0u); + BOOST_CHECK_EQUAL(decode("01"sv), 1u); + BOOST_CHECK_EQUAL(decode("7f"sv), 0x7Fu); + BOOST_CHECK_EQUAL(decode("8180"sv), 0x80u); + BOOST_CHECK_EQUAL(decode("820400"sv), 0x400u); + BOOST_CHECK_EQUAL(decode("83ffccb5"sv), 0xFFCCB5u); + BOOST_CHECK_EQUAL(decode("84ffccb5dd"sv), 0xFFCCB5DDu); + BOOST_CHECK_EQUAL(decode("85CE05050505"sv), 0xCE05050505u); + BOOST_CHECK_EQUAL(decode("85ffccb5ddff"sv), 0xFFCCB5DDFFu); + BOOST_CHECK_EQUAL(decode("86ffccb5ddffee"sv), 0xFFCCB5DDFFEEu); + BOOST_CHECK_EQUAL(decode("87ffccb5ddffee14"sv), 0xFFCCB5DDFFEE14u); + BOOST_CHECK_EQUAL(decode("88ffccb5ddffee1483"sv), 0xFFCCB5DDFFEE1483u); + + decode("C0", UnexpectedList); + decode("8105", NonCanonicalSize); + decode("B8020004", NonCanonicalSize); + decode("8AFFFFFFFFFFFFFFFFFF7C", Overflow); +} + +BOOST_AUTO_TEST_CASE(uint256Decode) +{ + BOOST_CHECK_EQUAL(decode("80"sv), 0u); + BOOST_CHECK_EQUAL(decode("01"sv), 1u); + BOOST_CHECK_EQUAL(decode("7f"sv), 0x7Fu); + BOOST_CHECK_EQUAL(decode("8180"sv), 0x80u); + BOOST_CHECK_EQUAL(decode("820400"sv), 0x400u); + BOOST_CHECK_EQUAL(decode("83ffccb5"sv), 0xFFCCB5u); + BOOST_CHECK_EQUAL(decode("84ffccb5dd"sv), 0xFFCCB5DDu); + BOOST_CHECK_EQUAL(decode("85ffccb5ddff"sv), 0xFFCCB5DDFFu); + BOOST_CHECK_EQUAL(decode("86ffccb5ddffee"sv), 0xFFCCB5DDFFEEu); + BOOST_CHECK_EQUAL(decode("87ffccb5ddffee14"sv), 0xFFCCB5DDFFEE14u); + BOOST_CHECK_EQUAL(decode("88ffccb5ddffee1483"sv), 0xFFCCB5DDFFEE1483u); + BOOST_CHECK_EQUAL(decode("8AFFFFFFFFFFFFFFFFFF7C"sv), u256("0xFFFFFFFFFFFFFFFFFF7C")); + BOOST_CHECK_EQUAL(decode("8f10203e405060708090a0b0c0d0e0f2"sv), + u256("0x10203E405060708090A0B0C0D0E0F2")); + BOOST_CHECK_EQUAL(decode("9c0100020003000400050006000700080009000a0b4b000c000d000e01"sv), + u256("0x0100020003000400050006000700080009000A0B4B000C000D000E01")); + + decode("8BFFFFFFFFFFFFFFFFFF7C"sv, InputTooShort); + decode("C0"sv, UnexpectedList); + decode("8105"sv, NonCanonicalSize); + decode("B8020004"sv, NonCanonicalSize); + decode( + "A101000000000000000000000000000000000000008B000000000000000000000000"sv, Overflow); +} + +BOOST_AUTO_TEST_CASE(vectorsDecode) +{ + BOOST_CHECK(checkVectorEqual(decode>("c0"sv), std::vector{})); + BOOST_CHECK(checkVectorEqual(decode>("c883ffccb583ffc0b5"sv), + std::vector({0xFFCCB5, 0xFFC0B5}))); + BOOST_CHECK(checkVectorEqual(decode>("c883ffccb583ffc0b5"sv), + std::vector({0xFFCCB5, 0xFFC0B5}))); + BOOST_CHECK(checkVectorEqual(decode>("c88363617483646f67"sv), + std::vector({"cat", "dog"}))); + BOOST_CHECK(checkVectorEqual(decode>("cc83646f6783676f6483636174"sv), + std::vector({"dog", "god", "cat"}))); + // multi vector test 1 + { + std::string zw; + std::vector v; + uint16_t one; + auto error = decode("c6827a77c10401"sv, zw, v, one); + BOOST_CHECK(!error); + BOOST_CHECK_EQUAL(zw, "zw"); + BOOST_CHECK_EQUAL(v.size(), 1u); + BOOST_CHECK_EQUAL(v[0], 4u); + BOOST_CHECK_EQUAL(one, 1u); + } + // multi vector test 2 + { + std::vector> a; + std::vector b; + auto error = decode("c4c2c0c0c0"sv, a, b); + BOOST_CHECK(!error); + BOOST_CHECK_EQUAL(a.size(), 2u); + BOOST_CHECK_EQUAL(a[0].size(), 0u); + BOOST_CHECK_EQUAL(a[1].size(), 0u); + BOOST_CHECK_EQUAL(b.size(), 0u); + } + // multi vector test 3 + { + std::vector b; + std::vector> a2; + std::vector>> a3; + auto error = decode("c7c0c1c0c3c0c1c0"sv, b, a2, a3); + BOOST_CHECK(!error); + BOOST_CHECK_EQUAL(a2.size(), 1u); + BOOST_CHECK_EQUAL(a2[0].size(), 0u); + BOOST_CHECK_EQUAL(a3.size(), 2u); + BOOST_CHECK_EQUAL(a3[0].size(), 0u); + BOOST_CHECK_EQUAL(a3[1].size(), 1u); + BOOST_CHECK_EQUAL(a3[1][0].size(), 0u); + BOOST_CHECK_EQUAL(b.size(), 0u); + } +} + + BOOST_AUTO_TEST_SUITE_END() } // namespace bcos::test \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp new file mode 100644 index 0000000000..8f40a563cb --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp @@ -0,0 +1,317 @@ +/** + * Copyright (C) 2022 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file Web3Transaction.cpp + * @author: kyonGuo + * @date 2024/4/8 + */ + +#include "Web3Transaction.h" + +#include + +namespace bcos +{ +using codec::rlp::decode; +using codec::rlp::encode; +using codec::rlp::header; +using codec::rlp::length; +bcos::bytes bcos::Web3Transaction::encode() const +{ + bcos::bytes out; + if (type == TransactionType::Legacy) + { + // rlp([nonce, gasPrice, gasLimit, to, value, data]) + codec::rlp::encodeHeader(out, codec::rlp::headerForSign(*this)); + codec::rlp::encode(out, nonce); + // for legacy tx, it means gas price + codec::rlp::encode(out, maxFeePerGas); + codec::rlp::encode(out, gasLimit); + codec::rlp::encode(out, to.ref()); + codec::rlp::encode(out, value); + codec::rlp::encode(out, data); + if (chainId) + { + // EIP-155 + codec::rlp::encode(out, chainId.value()); + codec::rlp::encode(out, 0u); + codec::rlp::encode(out, 0u); + } + } + else + { + // EIP2930: 0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList]) + + // EIP1559: 0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, + // gas_limit, destination, amount, data, access_list]) + + // EIP4844: 0x03 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, + // gas_limit, to, value, data, access_list, max_fee_per_blob_gas, blob_versioned_hashes]) + out.push_back(static_cast(type)); + codec::rlp::encodeHeader(out, codec::rlp::headerForSign(*this)); + codec::rlp::encode(out, chainId.value_or(0)); + codec::rlp::encode(out, nonce); + if (type != TransactionType::EIP2930) + { + codec::rlp::encode(out, maxFeePerGas); + } + // for EIP2930 it means gasPrice; for EIP1559 and EIP4844, it means max priority fee per gas + codec::rlp::encode(out, maxFeePerGas); + codec::rlp::encode(out, gasLimit); + codec::rlp::encode(out, to.ref()); + codec::rlp::encode(out, value); + codec::rlp::encode(out, data); + codec::rlp::encode(out, accessList); + if (type == TransactionType::EIP4844) + { + codec::rlp::encode(out, maxFeePerBlobGas); + codec::rlp::encode(out, blobVersionedHashes); + } + } + return out; +} + +bcos::crypto::HashType bcos::Web3Transaction::hash() const +{ + bcos::bytes encoded{}; + codec::rlp::encode(encoded, *this); + return bcos::crypto::keccak256Hash(bcos::ref(encoded)); +} +namespace codec::rlp +{ +Header header(const AccessListEntry& entry) noexcept +{ + return {.isList = true, .payloadLength = Address::SIZE + 1 + length(entry.storageKeys)}; +} + +size_t length(AccessListEntry const& entry) noexcept +{ + auto head = header(entry); + return lengthOfLength(head.payloadLength) + head.payloadLength; +} +Header headerTxBase(const Web3Transaction& tx) noexcept +{ + Header h{.isList = true}; + + if (tx.type != TransactionType::Legacy) + { + h.payloadLength += length(tx.chainId.value_or(0)); + } + + h.payloadLength += length(tx.nonce); + if (tx.type == TransactionType::EIP1559 || tx.type == TransactionType::EIP4844) + { + h.payloadLength += length(tx.maxPriorityFeePerGas); + } + h.payloadLength += length(tx.maxFeePerGas); + h.payloadLength += length(tx.gasLimit); + h.payloadLength += tx.to ? (Address::SIZE + 1) : 1; + h.payloadLength += length(tx.value); + h.payloadLength += length(tx.data); + + if (tx.type != TransactionType::Legacy) + { + h.payloadLength += codec::rlp::length(tx.accessList); + if (tx.type == TransactionType::EIP4844) + { + h.payloadLength += length(tx.maxFeePerBlobGas); + h.payloadLength += length(tx.blobVersionedHashes); + } + } + + return h; +} +Header header(Web3Transaction const& tx) noexcept +{ + auto header = headerTxBase(tx); + header.payloadLength += (tx.type == TransactionType::Legacy) ? length(tx.getSignatureV()) : 1; + header.payloadLength += length(tx.signatureR); + header.payloadLength += length(tx.signatureS); + return header; +} +Header headerForSign(Web3Transaction const& tx) noexcept +{ + auto header = headerTxBase(tx); + if (tx.type == TransactionType::Legacy && tx.chainId) + { + header.payloadLength += length(tx.chainId.value()) + 2; + } + return header; +} +size_t length(Web3Transaction const& tx) noexcept +{ + auto head = header(tx); + auto len = lengthOfLength(head.payloadLength) + head.payloadLength; + len = (tx.type == TransactionType::Legacy) ? len : lengthOfLength(len + 1) + len + 1; + return len; +} +void encode(bcos::bytes& out, const AccessListEntry& entry) noexcept +{ + encodeHeader(out, header(entry)); + encode(out, entry.account.ref()); + encode(out, entry.storageKeys); +} +void encode(bcos::bytes& out, const Web3Transaction& tx) noexcept +{ + if (tx.type == TransactionType::Legacy) + { + // rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s]) + encodeHeader(out, header(tx)); + encode(out, tx.nonce); + // for legacy tx, it means gas price + encode(out, tx.maxFeePerGas); + encode(out, tx.gasLimit); + encode(out, tx.to.ref()); + encode(out, tx.value); + encode(out, tx.data); + } + else + { + // EIP2930: 0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList, + // signatureYParity, signatureR, signatureS]) + + // EIP1559: 0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, + // gas_limit, destination, amount, data, access_list, signature_y_parity, signature_r, + // signature_s]) + + // EIP4844: 0x03 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, + // gas_limit, to, value, data, access_list, max_fee_per_blob_gas, blob_versioned_hashes, + // signature_y_parity, signature_r, signature_s]) + out.push_back(static_cast(tx.type)); + encodeHeader(out, header(tx)); + encode(out, tx.chainId.value_or(0)); + encode(out, tx.nonce); + if (tx.type != TransactionType::EIP2930) + { + encode(out, tx.maxFeePerGas); + } + // for EIP2930 it means gasPrice; for EIP1559 and EIP4844, it means max priority fee per gas + encode(out, tx.maxFeePerGas); + encode(out, tx.gasLimit); + encode(out, tx.to.ref()); + encode(out, tx.value); + encode(out, tx.data); + encode(out, tx.accessList); + if (tx.type == TransactionType::EIP4844) + { + encode(out, tx.maxFeePerBlobGas); + encode(out, tx.blobVersionedHashes); + } + } + encode(out, tx.getSignatureV()); + encode(out, tx.signatureR); + encode(out, tx.signatureS); +} +bcos::Error::UniquePtr decode(bcos::bytesRef& in, AccessListEntry& out) noexcept +{ + return decode(in, out.account, out.storageKeys); +} +bcos::Error::UniquePtr decode(bcos::bytesRef& in, Web3Transaction& out) noexcept +{ + if (in.empty()) + { + return BCOS_ERROR_UNIQUE_PTR(InputTooShort, "Input too short"); + } + if (auto const& firstByte = in[0]; 0 < firstByte && firstByte < BYTES_HEAD_BASE) + { + // EIP-2718: Transaction Type + // EIP2930: 0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList, + // signatureYParity, signatureR, signatureS]) + + // EIP1559: 0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, + // gas_limit, destination, amount, data, access_list, signature_y_parity, signature_r, + // signature_s]) + + // EIP4844: 0x03 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, + // gas_limit, to, value, data, access_list, max_fee_per_blob_gas, blob_versioned_hashes, + // signature_y_parity, signature_r, signature_s]) + + out.type = static_cast(firstByte); + in = in.getCroppedData(1); + auto&& [e, header] = decodeHeader(in); + if (e != nullptr) + { + return std::move(e); + } + if (!header.isList) + { + return BCOS_ERROR_UNIQUE_PTR(UnexpectedList, "Unexpected list"); + } + if (auto error = decodeItems(in, out.chainId.value(), out.nonce, out.maxFeePerGas); !error) + { + return error; + } + if (out.type == TransactionType::EIP2930) + { + out.maxFeePerGas = out.maxPriorityFeePerGas; + } + else if (auto error = decode(in, out.maxPriorityFeePerGas); !error) + { + return error; + } + + if (auto error = decodeItems(in, out.gasLimit, out.to, out.value, out.data, out.accessList); + !error) + { + return error; + } + + if (out.type == TransactionType::EIP4844) + { + if (auto error = decodeItems(in, out.maxFeePerBlobGas, out.blobVersionedHashes); !error) + { + return error; + } + } + + return decodeItems(in, out.signatureV, out.signatureR, out.signatureS); + } + // rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s]) + auto&& [error, header] = decodeHeader(in); + if (error != nullptr) + { + return std::move(error); + } + if (!header.isList) + { + return BCOS_ERROR_UNIQUE_PTR(UnexpectedList, "Unexpected list"); + } + out.type = TransactionType::Legacy; + auto decodeError = decodeItems(in, out.nonce, out.maxPriorityFeePerGas, out.gasLimit, out.to, + out.value, out.data, out.signatureV, out.signatureR, out.signatureS); + out.maxFeePerGas = out.maxPriorityFeePerGas; + auto v = out.signatureV; + if (v == 27 || v == 28) + { + // pre EIP-155 + out.chainId = std::nullopt; + out.signatureV = v - 27; + } + else if (v < 35) + { + return BCOS_ERROR_UNIQUE_PTR(InvalidVInSignature, "Invalid V in signature"); + } + else + { + // https://eips.ethereum.org/EIPS/eip-155 + // Find chain_id and y_parity ∈ {0, 1} such that + // v = chain_id * 2 + 35 + y_parity + out.signatureV = (v - 35) % 2; + out.chainId = ((v - 35) >> 1); + } + return decodeError; +} +} // namespace codec::rlp +} // namespace bcos diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h new file mode 100644 index 0000000000..cdd4c47169 --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h @@ -0,0 +1,104 @@ +/** + * Copyright (C) 2022 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file Web3Transaction.h + * @author: kyonGuo + * @date 2024/4/8 + */ + +#pragma once +#include +#include +#include +#include +#include +#include +namespace bcos +{ + +// EIP-2718 transaction type +// https://github.com/ethereum/eth1.0-specs/tree/master/lists/signature-types +enum class TransactionType : uint8_t +{ + Legacy = 0, + EIP2930 = 1, // https://eips.ethereum.org/EIPS/eip-2930 + EIP1559 = 2, // https://eips.ethereum.org/EIPS/eip-1559 + EIP4844 = 3, // https://eips.ethereum.org/EIPS/eip-4844 +}; + +// EIP-2930: Access lists +struct AccessListEntry +{ + Address account{}; + std::vector storageKeys{}; +}; + +class Web3Transaction +{ +public: + Web3Transaction() = default; + ~Web3Transaction() = default; + Web3Transaction(const Web3Transaction&) = delete; + Web3Transaction(Web3Transaction&&) = default; + Web3Transaction& operator=(const Web3Transaction&) = delete; + Web3Transaction& operator=(Web3Transaction&&) = default; + + bcos::bytes encode() const; + bcos::crypto::HashType hash() const; + + uint64_t getSignatureV() const + { + // EIP-155: Simple replay attack protection + if (chainId.has_value()) + { + return chainId.value() * 2 + 35 + signatureV; + } + return signatureV + 27; + } + + std::optional chainId{std::nullopt}; // nullopt means a pre-EIP-155 transaction + TransactionType type{TransactionType::Legacy}; + Address to{}; + bcos::bytes data{}; + uint64_t value{0}; + uint64_t nonce{0}; + uint64_t gasLimit{0}; + // EIP-2930: Optional access lists + std::vector accessList{}; + // EIP-1559: Fee market change for ETH 1.0 chain + uint64_t maxPriorityFeePerGas{0}; // for legacy tx, it stands for gasPrice + uint64_t maxFeePerGas{0}; + // EIP-4844: Shard Blob Transactions + uint64_t maxFeePerBlobGas{0}; + std::vector blobVersionedHashes{}; + bcos::bytes signatureR{}; + bcos::bytes signatureS{}; + uint64_t signatureV{0}; +}; +namespace codec::rlp +{ +Header header(const AccessListEntry& entry) noexcept; +void encode(bcos::bytes& out, const AccessListEntry&) noexcept; +size_t length(const AccessListEntry&) noexcept; + +size_t length(const Web3Transaction&) noexcept; +Header headerForSign(const Web3Transaction& tx) noexcept; +Header headerTxBase(const Web3Transaction& tx) noexcept; +Header header(const Web3Transaction& tx) noexcept; +void encode(bcos::bytes& out, const Web3Transaction&) noexcept; +bcos::Error::UniquePtr decode(bcos::bytesRef& in, AccessListEntry&) noexcept; +bcos::Error::UniquePtr decode(bcos::bytesRef& in, Web3Transaction&) noexcept; +} // namespace codec::rlp +} // namespace bcos diff --git a/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp b/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp new file mode 100644 index 0000000000..d42b3ea710 --- /dev/null +++ b/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp @@ -0,0 +1,70 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file Web3TypeTest.cpp + * @author: kyonGuo + * @date 2024/4/9 + */ + +#include "../common/RPCFixture.h" +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::rpc; +using namespace bcos::codec::rlp; +namespace bcos::test +{ +BOOST_FIXTURE_TEST_SUITE(testWeb3Type, RPCFixture) +BOOST_AUTO_TEST_CASE(testTransactionDecode) +{ + // clang-format off + constexpr std::string_view rawTx = "0xf89b0c8504a817c80082520894727fc6a68321b754475c668a6abfb6e9e71c169a888ac7230489e80000afa9059cbb000000000213ed0f886efd100b67c7e4ec0a85a7d20dc971600000000000000000000015af1d78b58c400026a0be67e0a07db67da8d446f76add590e54b6e92cb6b8f9835aeb67540579a27717a02d690516512020171c1ec870f6ff45398cc8609250326be89915fb538e7bd718"; + // constexpr std::string_view rawTx = "0xf8ac82017c8504a817c800835fefd89409d07ecb4d6f32e91503c04b192e3bdeb7f857f480b8442c7128d700000000000000000000000000000000000000000000000000009bc24e89949a00000000000000000000000000000000000000000000000000000000000000001ba0cd372eb41b6b4e9e576233bb29c1492e0329fac1331f492a69e4a1b586a1a28ba032950cc4184ca8b0d45b24d13345157b4153d7ccc0d187dbab018be07726d186"; + // clang-format on + auto bytes = fromHexWithPrefix(rawTx); + auto bRef = bcos::ref(bytes); + Web3Transaction tx{}; + auto e = codec::rlp::decode(bRef, tx); + BOOST_CHECK(!e); + BOOST_CHECK(tx.type == TransactionType::Legacy); + BOOST_CHECK(tx.chainId.has_value()); + BOOST_CHECK_EQUAL(tx.chainId.value(), 1); + BOOST_CHECK_EQUAL(tx.nonce, 12); + BOOST_CHECK_EQUAL(tx.nonce, 12); + BOOST_CHECK_EQUAL(tx.maxPriorityFeePerGas, 20000000000); + BOOST_CHECK_EQUAL(tx.maxFeePerGas, 20000000000); + BOOST_CHECK_EQUAL(tx.gasLimit, 21000); + BOOST_CHECK_EQUAL(tx.to, Address("0x727fc6a68321b754475c668a6abfb6e9e71c169a")); + BOOST_CHECK_EQUAL(tx.value, 10000000000000000000ull); + BOOST_CHECK_EQUAL(toHex(tx.data), + "a9059cbb000000000213ed0f886efd100b67c7e4ec0a85a7d20dc9" + "71600000000000000000000015af1d78b58c4000"); + BOOST_CHECK_EQUAL( + toHex(tx.signatureR), "be67e0a07db67da8d446f76add590e54b6e92cb6b8f9835aeb67540579a27717"); + BOOST_CHECK_EQUAL( + toHex(tx.signatureS), "2d690516512020171c1ec870f6ff45398cc8609250326be89915fb538e7bd718"); + BOOST_CHECK_EQUAL(tx.getSignatureV(), tx.chainId.value() * 2 + 35 + 1); + + bcos::bytes encoded{}; + codec::rlp::encode(encoded, tx); + auto rawTx2 = toHexStringWithPrefix(encoded); + BOOST_CHECK_EQUAL(rawTx, rawTx2); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test \ No newline at end of file diff --git a/bcos-utilities/bcos-utilities/RefDataContainer.h b/bcos-utilities/bcos-utilities/RefDataContainer.h index 0406b3a936..82205f3a03 100644 --- a/bcos-utilities/bcos-utilities/RefDataContainer.h +++ b/bcos-utilities/bcos-utilities/RefDataContainer.h @@ -18,6 +18,7 @@ * @date 2021-02-24 */ #pragma once +#include #include #include #include @@ -101,6 +102,12 @@ class RefDataContainer return std::string_view((char const*)m_dataPointer, m_dataCount * sizeof(T)); } + template + String toStringLike() const + { + return String((char const*)m_dataPointer, m_dataCount * sizeof(T)); + } + bool empty() const { return (m_dataCount == 0); } RefDataContainer getCroppedData(size_t _startIndex, size_t _count) const { From 047fd1ea8b90f44c271c59e8d402a5ea97d89b9a Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Tue, 9 Apr 2024 14:57:07 +0800 Subject: [PATCH 09/39] (rpc): fix web3 transaction encode decode bug, add ut for eip2930, eip1559, eip4844. (#4352) --- bcos-codec/bcos-codec/rlp/Common.h | 3 +- bcos-codec/bcos-codec/rlp/RLPDecode.h | 12 +- .../web3jsonrpc/model/Web3Transaction.cpp | 28 +++-- .../web3jsonrpc/model/Web3Transaction.h | 4 + bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp | 115 +++++++++++++++++- 5 files changed, 142 insertions(+), 20 deletions(-) diff --git a/bcos-codec/bcos-codec/rlp/Common.h b/bcos-codec/bcos-codec/rlp/Common.h index 4087407978..7482bb6d7c 100644 --- a/bcos-codec/bcos-codec/rlp/Common.h +++ b/bcos-codec/bcos-codec/rlp/Common.h @@ -77,7 +77,8 @@ inline size_t lengthOfLength(std::unsigned_integral auto payloadLength) noexcept } else { - auto significantBytes = (sizeof(payloadLength) - std::countl_zero(payloadLength) + 7) / 8; + auto significantBytes = + (sizeof(payloadLength) * 8 - std::countl_zero(payloadLength) + 7) / 8; return 1 + significantBytes; } } diff --git a/bcos-codec/bcos-codec/rlp/RLPDecode.h b/bcos-codec/bcos-codec/rlp/RLPDecode.h index 0f32cb9e02..cad0bf2c6e 100644 --- a/bcos-codec/bcos-codec/rlp/RLPDecode.h +++ b/bcos-codec/bcos-codec/rlp/RLPDecode.h @@ -151,10 +151,10 @@ inline bcos::Error::UniquePtr decode(bytesRef& from, bcos::concepts::ByteBuffer { to = FixedBytes<20>{from.getCroppedData(0, header.payloadLength)}; } - // else - // { - // static_assert("Unsupported type"); - // } + else + { + static_assert(!sizeof(to), "Unsupported type"); + } from = from.getCroppedData(header.payloadLength); return nullptr; } @@ -257,10 +257,6 @@ inline bcos::Error::UniquePtr decode(bytesRef& from, Arg1& arg1, Arg2& arg2, Arg return BCOS_ERROR_UNIQUE_PTR(DecodingError::UnexpectedString, "Unexpected string"); } const uint64_t leftover{from.size() - header.payloadLength}; - if (leftover) - { - return BCOS_ERROR_UNIQUE_PTR(DecodingError::InputTooLong, "Input data is too long"); - } if (auto decodeError = decodeItems(from, arg1, arg2, args...); decodeError != nullptr) { diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp index 8f40a563cb..cd30e9ffdc 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp @@ -93,7 +93,8 @@ namespace codec::rlp { Header header(const AccessListEntry& entry) noexcept { - return {.isList = true, .payloadLength = Address::SIZE + 1 + length(entry.storageKeys)}; + auto len = length(entry.storageKeys); + return {.isList = true, .payloadLength = Address::SIZE + 1 + len}; } size_t length(AccessListEntry const& entry) noexcept @@ -176,6 +177,9 @@ void encode(bcos::bytes& out, const Web3Transaction& tx) noexcept encode(out, tx.to.ref()); encode(out, tx.value); encode(out, tx.data); + encode(out, tx.getSignatureV()); + encode(out, tx.signatureR); + encode(out, tx.signatureS); } else { @@ -195,7 +199,7 @@ void encode(bcos::bytes& out, const Web3Transaction& tx) noexcept encode(out, tx.nonce); if (tx.type != TransactionType::EIP2930) { - encode(out, tx.maxFeePerGas); + encode(out, tx.maxPriorityFeePerGas); } // for EIP2930 it means gasPrice; for EIP1559 and EIP4844, it means max priority fee per gas encode(out, tx.maxFeePerGas); @@ -209,10 +213,10 @@ void encode(bcos::bytes& out, const Web3Transaction& tx) noexcept encode(out, tx.maxFeePerBlobGas); encode(out, tx.blobVersionedHashes); } + encode(out, tx.signatureV); + encode(out, tx.signatureR); + encode(out, tx.signatureS); } - encode(out, tx.getSignatureV()); - encode(out, tx.signatureR); - encode(out, tx.signatureS); } bcos::Error::UniquePtr decode(bcos::bytesRef& in, AccessListEntry& out) noexcept { @@ -247,30 +251,34 @@ bcos::Error::UniquePtr decode(bcos::bytesRef& in, Web3Transaction& out) noexcept } if (!header.isList) { - return BCOS_ERROR_UNIQUE_PTR(UnexpectedList, "Unexpected list"); + return BCOS_ERROR_UNIQUE_PTR(UnexpectedString, "Unexpected String"); } - if (auto error = decodeItems(in, out.chainId.value(), out.nonce, out.maxFeePerGas); !error) + uint64_t chainId = 0; + if (auto error = decodeItems(in, chainId, out.nonce, out.maxPriorityFeePerGas); + error != nullptr) { return error; } + out.chainId.emplace(chainId); if (out.type == TransactionType::EIP2930) { out.maxFeePerGas = out.maxPriorityFeePerGas; } - else if (auto error = decode(in, out.maxPriorityFeePerGas); !error) + else if (auto error = decode(in, out.maxFeePerGas); error != nullptr) { return error; } if (auto error = decodeItems(in, out.gasLimit, out.to, out.value, out.data, out.accessList); - !error) + error != nullptr) { return error; } if (out.type == TransactionType::EIP4844) { - if (auto error = decodeItems(in, out.maxFeePerBlobGas, out.blobVersionedHashes); !error) + if (auto error = decodeItems(in, out.maxFeePerBlobGas, out.blobVersionedHashes); + error != nullptr) { return error; } diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h index cdd4c47169..87ed7f3132 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h @@ -43,6 +43,10 @@ struct AccessListEntry { Address account{}; std::vector storageKeys{}; + friend bool operator==(const AccessListEntry& lhs, const AccessListEntry& rhs) noexcept + { + return lhs.account == rhs.account && lhs.storageKeys == rhs.storageKeys; + } }; class Web3Transaction diff --git a/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp b/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp index d42b3ea710..4d72710952 100644 --- a/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp +++ b/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp @@ -30,8 +30,16 @@ using namespace bcos::rpc; using namespace bcos::codec::rlp; namespace bcos::test { +static const std::vector s_accessList{ + {Address("0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae"), + { + HashType("0x0000000000000000000000000000000000000000000000000000000000000003"), + HashType("0x0000000000000000000000000000000000000000000000000000000000000007"), + }}, + {Address("0xbb9bc244d798123fde783fcc1c72d3bb8c189413"), {}}, +}; BOOST_FIXTURE_TEST_SUITE(testWeb3Type, RPCFixture) -BOOST_AUTO_TEST_CASE(testTransactionDecode) +BOOST_AUTO_TEST_CASE(testLegacyTransactionDecode) { // clang-format off constexpr std::string_view rawTx = "0xf89b0c8504a817c80082520894727fc6a68321b754475c668a6abfb6e9e71c169a888ac7230489e80000afa9059cbb000000000213ed0f886efd100b67c7e4ec0a85a7d20dc971600000000000000000000015af1d78b58c400026a0be67e0a07db67da8d446f76add590e54b6e92cb6b8f9835aeb67540579a27717a02d690516512020171c1ec870f6ff45398cc8609250326be89915fb538e7bd718"; @@ -66,5 +74,110 @@ BOOST_AUTO_TEST_CASE(testTransactionDecode) auto rawTx2 = toHexStringWithPrefix(encoded); BOOST_CHECK_EQUAL(rawTx, rawTx2); } + +BOOST_AUTO_TEST_CASE(testEIP2930Transaction) +{ + // clang-format off + constexpr std::string_view rawTx = "0x01f8f205078506fc23ac008357b58494811a752c8cd697e3cb27279c330ed1ada745a8d7881bc16d674ec80000906ebaf477f83e051589c1188bcc6ddccdf872f85994de0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000007d694bb9bc244d798123fde783fcc1c72d3bb8c189413c080a036b241b061a36a32ab7fe86c7aa9eb592dd59018cd0443adc0903590c16b02b0a05edcc541b4741c5cc6dd347c5ed9577ef293a62787b4510465fadbfe39ee4094"; + // clang-format on + auto bytes = fromHexWithPrefix(rawTx); + auto bRef = bcos::ref(bytes); + Web3Transaction tx{}; + auto e = codec::rlp::decode(bRef, tx); + BOOST_CHECK(e == nullptr); + BOOST_CHECK(tx.type == TransactionType::EIP2930); + BOOST_CHECK(tx.chainId.has_value()); + BOOST_CHECK_EQUAL(tx.chainId.value(), 5); + BOOST_CHECK_EQUAL(tx.nonce, 7); + BOOST_CHECK_EQUAL(tx.maxPriorityFeePerGas, 30000000000); + BOOST_CHECK_EQUAL(tx.maxFeePerGas, 30000000000); + BOOST_CHECK_EQUAL(tx.gasLimit, 5748100); + BOOST_CHECK_EQUAL(tx.to, Address("0x811a752c8cd697e3cb27279c330ed1ada745a8d7")); + BOOST_CHECK_EQUAL(tx.value, 2000000000000000000ull); + BOOST_CHECK_EQUAL(toHex(tx.data), "6ebaf477f83e051589c1188bcc6ddccd"); + BOOST_CHECK_EQUAL(tx.getSignatureV(), tx.chainId.value() * 2 + 35); + BOOST_CHECK_EQUAL( + toHex(tx.signatureR), "36b241b061a36a32ab7fe86c7aa9eb592dd59018cd0443adc0903590c16b02b0"); + BOOST_CHECK_EQUAL( + toHex(tx.signatureS), "5edcc541b4741c5cc6dd347c5ed9577ef293a62787b4510465fadbfe39ee4094"); + BOOST_CHECK(tx.accessList == s_accessList); + + bcos::bytes encoded{}; + codec::rlp::encode(encoded, tx); + auto rawTx2 = toHexStringWithPrefix(encoded); + BOOST_CHECK_EQUAL(rawTx, rawTx2); +} + +BOOST_AUTO_TEST_CASE(testEIP1559Transaction) +{ + // clang-format off + constexpr std::string_view rawTx = "0x02f8f805078502540be4008506fc23ac008357b58494811a752c8cd697e3cb27279c330ed1ada745a8d7881bc16d674ec80000906ebaf477f83e051589c1188bcc6ddccdf872f85994de0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000007d694bb9bc244d798123fde783fcc1c72d3bb8c189413c080a036b241b061a36a32ab7fe86c7aa9eb592dd59018cd0443adc0903590c16b02b0a05edcc541b4741c5cc6dd347c5ed9577ef293a62787b4510465fadbfe39ee4094"; + // clang-format on + auto bytes = fromHexWithPrefix(rawTx); + auto bRef = bcos::ref(bytes); + Web3Transaction tx{}; + auto e = codec::rlp::decode(bRef, tx); + BOOST_CHECK(e == nullptr); + BOOST_CHECK(tx.type == TransactionType::EIP1559); + BOOST_CHECK(tx.chainId.has_value()); + BOOST_CHECK_EQUAL(tx.chainId.value(), 5); + BOOST_CHECK_EQUAL(tx.nonce, 7); + BOOST_CHECK_EQUAL(tx.maxPriorityFeePerGas, 10000000000); + BOOST_CHECK_EQUAL(tx.maxFeePerGas, 30000000000); + BOOST_CHECK_EQUAL(tx.gasLimit, 5748100); + BOOST_CHECK_EQUAL(tx.to, Address("0x811a752c8cd697e3cb27279c330ed1ada745a8d7")); + BOOST_CHECK_EQUAL(tx.value, 2000000000000000000ull); + BOOST_CHECK_EQUAL(toHex(tx.data), "6ebaf477f83e051589c1188bcc6ddccd"); + BOOST_CHECK_EQUAL(tx.getSignatureV(), tx.chainId.value() * 2 + 35); + BOOST_CHECK_EQUAL( + toHex(tx.signatureR), "36b241b061a36a32ab7fe86c7aa9eb592dd59018cd0443adc0903590c16b02b0"); + BOOST_CHECK_EQUAL( + toHex(tx.signatureS), "5edcc541b4741c5cc6dd347c5ed9577ef293a62787b4510465fadbfe39ee4094"); + BOOST_CHECK(tx.accessList == s_accessList); + + bcos::bytes encoded{}; + codec::rlp::encode(encoded, tx); + auto rawTx2 = toHexStringWithPrefix(encoded); + BOOST_CHECK_EQUAL(rawTx, rawTx2); +} + +BOOST_AUTO_TEST_CASE(testEIP4844Transaction) +{ + // clang-format off + constexpr std::string_view rawTx = "0x03f9012705078502540be4008506fc23ac008357b58494811a752c8cd697e3cb27279c330ed1ada745a8d7808204f7f872f85994de0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000007d694bb9bc244d798123fde783fcc1c72d3bb8c189413c07bf842a0c6bdd1de713471bd6cfa62dd8b5a5b42969ed09e26212d3377f3f8426d8ec210a08aaeccaf3873d07cef005aca28c39f8a9f8bdb1ec8d79ffc25afc0a4fa2ab73601a036b241b061a36a32ab7fe86c7aa9eb592dd59018cd0443adc0903590c16b02b0a05edcc541b4741c5cc6dd347c5ed9577ef293a62787b4510465fadbfe39ee4094"; + // clang-format on + auto bytes = fromHexWithPrefix(rawTx); + auto bRef = bcos::ref(bytes); + Web3Transaction tx{}; + auto e = codec::rlp::decode(bRef, tx); + BOOST_CHECK(e == nullptr); + BOOST_CHECK(tx.type == TransactionType::EIP4844); + BOOST_CHECK(tx.chainId.has_value()); + BOOST_CHECK_EQUAL(tx.chainId.value(), 5); + BOOST_CHECK_EQUAL(tx.nonce, 7); + BOOST_CHECK_EQUAL(tx.maxPriorityFeePerGas, 10000000000); + BOOST_CHECK_EQUAL(tx.maxFeePerGas, 30000000000); + BOOST_CHECK_EQUAL(tx.gasLimit, 5748100); + BOOST_CHECK_EQUAL(tx.to, Address("0x811a752c8cd697e3cb27279c330ed1ada745a8d7")); + BOOST_CHECK_EQUAL(tx.value, 0); + BOOST_CHECK_EQUAL(toHex(tx.data), "04f7"); + BOOST_CHECK_EQUAL(tx.maxFeePerBlobGas, 123); + BOOST_CHECK_EQUAL(tx.blobVersionedHashes.size(), 2); + BOOST_CHECK_EQUAL(toHex(tx.blobVersionedHashes[0]), + "c6bdd1de713471bd6cfa62dd8b5a5b42969ed09e26212d3377f3f8426d8ec210"); + BOOST_CHECK_EQUAL(toHex(tx.blobVersionedHashes[1]), + "8aaeccaf3873d07cef005aca28c39f8a9f8bdb1ec8d79ffc25afc0a4fa2ab736"); + BOOST_CHECK_EQUAL(tx.getSignatureV(), tx.chainId.value() * 2 + 35 + 1); + BOOST_CHECK_EQUAL( + toHex(tx.signatureR), "36b241b061a36a32ab7fe86c7aa9eb592dd59018cd0443adc0903590c16b02b0"); + BOOST_CHECK_EQUAL( + toHex(tx.signatureS), "5edcc541b4741c5cc6dd347c5ed9577ef293a62787b4510465fadbfe39ee4094"); + BOOST_CHECK(tx.accessList == s_accessList); + + bcos::bytes encoded{}; + codec::rlp::encode(encoded, tx); + auto rawTx2 = toHexStringWithPrefix(encoded); + BOOST_CHECK_EQUAL(rawTx, rawTx2); +} BOOST_AUTO_TEST_SUITE_END() } // namespace bcos::test \ No newline at end of file From 4eac37164b01aaad14e769770fbb3a070a84a3a1 Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Wed, 10 Apr 2024 11:21:49 +0800 Subject: [PATCH 10/39] (rpc,txpool): impl sendRawTransaction interface. (#4356) --- .../bcos-framework/protocol/Transaction.h | 31 +++- .../testutils/faker/FakeTransaction.h | 6 +- .../bcos-framework/txpool/TxPoolInterface.h | 6 + .../web3jsonrpc/endpoints/EthEndpoint.cpp | 63 ++++++-- .../web3jsonrpc/model/Web3Transaction.cpp | 38 ++++- .../web3jsonrpc/model/Web3Transaction.h | 20 ++- bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp | 147 ++++++++++++++++++ .../protocol/TransactionFactoryImpl.h | 41 ++--- .../protocol/TransactionImpl.cpp | 11 ++ .../protocol/TransactionImpl.h | 3 + bcos-txpool/bcos-txpool/TxPool.cpp | 6 + bcos-txpool/bcos-txpool/TxPool.h | 3 + .../interfaces/TxPoolStorageInterface.h | 2 + .../txpool/storage/MemoryStorage.cpp | 65 ++++++++ .../txpool/storage/MemoryStorage.h | 3 + .../txpool/validator/LedgerNonceChecker.cpp | 2 +- .../txpool/validator/TxValidator.cpp | 6 +- cmake/CompilerSettings.cmake | 2 +- 18 files changed, 409 insertions(+), 46 deletions(-) diff --git a/bcos-framework/bcos-framework/protocol/Transaction.h b/bcos-framework/bcos-framework/protocol/Transaction.h index dc5c347b2d..27ba0adcce 100644 --- a/bcos-framework/bcos-framework/protocol/Transaction.h +++ b/bcos-framework/bcos-framework/protocol/Transaction.h @@ -26,6 +26,7 @@ #if !ONLY_CPP_SDK #include #endif +#include #include #include #include @@ -35,7 +36,13 @@ namespace bcos::protocol { -enum TransactionType +enum class TransactionType : uint8_t +{ + BCOSTransaction = 0, + Web3Transacion = 1, +}; + +enum TransactionOp { NullTransaction = 0, ContractCreation, @@ -68,6 +75,7 @@ class Transaction virtual void decode(bytesConstRef _txData) = 0; virtual void encode(bcos::bytes& txData) const = 0; virtual bcos::crypto::HashType hash() const = 0; + virtual bcos::bytesConstRef extraTransactionBytes() const = 0; virtual void verify(crypto::Hash& hashImpl, crypto::SignatureCrypto& signatureImpl) const { @@ -80,8 +88,17 @@ class Transaction { return; } - - auto hashResult = hash(); + // based on type to switch recover sender + crypto::HashType hashResult; + if (type() == static_cast(TransactionType::BCOSTransaction)) + { + hashResult = hash(); + } + else if (type() == static_cast(TransactionType::Web3Transacion)) + { + auto bytes = extraTransactionBytes(); + hashResult = bcos::crypto::keccak256Hash(bytes); + } // check the signatures auto signature = signatureData(); auto ret = signatureImpl.recoverAddress(hashImpl, hashResult, signature); @@ -116,13 +133,15 @@ class Transaction virtual bcos::bytesConstRef input() const = 0; virtual int64_t importTime() const = 0; virtual void setImportTime(int64_t _importTime) = 0; - virtual TransactionType type() const + virtual uint8_t type() const = 0; + + virtual TransactionOp txOp() const { if (!to().empty()) { - return TransactionType::MessageCall; + return TransactionOp::MessageCall; } - return TransactionType::ContractCreation; + return TransactionOp::ContractCreation; } virtual void forceSender(const bcos::bytes& _sender) const = 0; virtual bcos::bytesConstRef signatureData() const = 0; diff --git a/bcos-framework/bcos-framework/testutils/faker/FakeTransaction.h b/bcos-framework/bcos-framework/testutils/faker/FakeTransaction.h index bf9e77035b..5c3422c647 100644 --- a/bcos-framework/bcos-framework/testutils/faker/FakeTransaction.h +++ b/bcos-framework/bcos-framework/testutils/faker/FakeTransaction.h @@ -69,7 +69,7 @@ inline void checkTransaction( // check the fields BOOST_CHECK(decodedTransaction->hash() == pbTransaction->hash()); BOOST_CHECK(decodedTransaction->sender() == pbTransaction->sender()); - BOOST_CHECK(decodedTransaction->type() == pbTransaction->type()); + BOOST_CHECK(decodedTransaction->txOp() == pbTransaction->txOp()); BOOST_CHECK(decodedTransaction->to() == pbTransaction->to()); // check the transaction hash fields BOOST_CHECK(decodedTransaction->input().toBytes() == pbTransaction->input().toBytes()); @@ -92,11 +92,11 @@ inline Transaction::Ptr testTransaction(CryptoSuite::Ptr _cryptoSuite, { if (_to.empty()) { - BOOST_CHECK(pbTransaction->type() == TransactionType::ContractCreation); + BOOST_CHECK(pbTransaction->txOp() == TransactionOp::ContractCreation); } else { - BOOST_CHECK(pbTransaction->type() == TransactionType::MessageCall); + BOOST_CHECK(pbTransaction->txOp() == TransactionOp::MessageCall); } BOOST_CHECK(pbTransaction->sender() == std::string_view((char*)addr.data(), 20)); } diff --git a/bcos-framework/bcos-framework/txpool/TxPoolInterface.h b/bcos-framework/bcos-framework/txpool/TxPoolInterface.h index 19fdce6fee..17ea5544e0 100644 --- a/bcos-framework/bcos-framework/txpool/TxPoolInterface.h +++ b/bcos-framework/bcos-framework/txpool/TxPoolInterface.h @@ -53,6 +53,12 @@ class TxPoolInterface BOOST_THROW_EXCEPTION(std::runtime_error("Unimplemented!")); } + virtual task::Task submitTransactionWithoutReceipt( + [[maybe_unused]] protocol::Transaction::Ptr transaction) + { + BOOST_THROW_EXCEPTION(std::runtime_error("Unimplemented!")); + } + virtual task::Task submitTransactionWithHook( [[maybe_unused]] protocol::Transaction::Ptr transaction, [[maybe_unused]] std::function afterInsertHook) diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp index 94adc33875..9b165c36bc 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp @@ -20,9 +20,14 @@ #include "EthEndpoint.h" +#include +#include +#include +#include #include #include #include +#include #include #include @@ -95,7 +100,7 @@ task::Task EthEndpoint::getBalance(const Json::Value& request, Json::Value { // params: address(DATA), blockNumber(QTY|TAG) // result: balance(QTY) - // FIXME: get balance from ledger + // TODO: get balance from ledger co_return; } task::Task EthEndpoint::getStorageAt(const Json::Value&, Json::Value&) @@ -110,7 +115,7 @@ task::Task EthEndpoint::getTransactionCount(const Json::Value& request, Js { // params: address(DATA), blockNumber(QTY|TAG) // result: nonce(QTY) - // FIXME: impliment getTransactionCount + // TODO: impliment getTransactionCount co_return; } task::Task EthEndpoint::getBlockTxCountByHash( @@ -161,42 +166,73 @@ task::Task EthEndpoint::getCode(const Json::Value&, Json::Value&) JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); co_return; } -task::Task EthEndpoint::sign(const Json::Value&, Json::Value&) +task::Task EthEndpoint::sign(const Json::Value&, Json::Value& response) { // params: address(DATA), message(DATA) // result: signature(DATA) - BOOST_THROW_EXCEPTION( - JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); + Json::Value result = "0x0"; + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::signTransaction(const Json::Value&, Json::Value&) +task::Task EthEndpoint::signTransaction(const Json::Value&, Json::Value& response) { // params: transaction(TX), address(DATA) // result: signedTransaction(DATA) - BOOST_THROW_EXCEPTION( - JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); + Json::Value result = "0x0"; + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::sendTransaction(const Json::Value&, Json::Value&) +task::Task EthEndpoint::sendTransaction(const Json::Value&, Json::Value& response) { // params: transaction(TX) // result: transactionHash(DATA) - BOOST_THROW_EXCEPTION( - JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); + Json::Value result = "0x0"; + buildJsonContent(result, response); co_return; } task::Task EthEndpoint::sendRawTransaction(const Json::Value& request, Json::Value& response) { // params: signedTransaction(DATA) // result: transactionHash(DATA) - // FIXME: impl this + auto txpool = m_nodeService->txpool(); + if (!txpool) [[unlikely]] + { + BOOST_THROW_EXCEPTION( + JsonRpcException(JsonRpcError::InternalError, "TXPool not available!")); + } + auto rawTx = toView(request[0U]); + auto rawTxBytes = fromHexWithPrefix(rawTx); + auto bytesRef = bcos::ref(rawTxBytes); + Web3Transaction web3Tx; + if (auto const error = codec::rlp::decode(bytesRef, web3Tx); error != nullptr) [[unlikely]] + { + BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParams, error->errorMessage())); + } + auto tarsTx = web3Tx.toTarsTransaction(); + + auto tx = std::make_shared( + [m_tx = std::move(tarsTx)]() mutable { return &m_tx; }); + + if (c_fileLogLevel == TRACE) + { + RPC_IMPL_LOG(TRACE) << LOG_DESC("sendRawTransaction") << web3Tx.toString(); + } + txpool->broadcastTransaction(*tx); + auto const txResult = co_await txpool->submitTransactionWithoutReceipt(std::move(tx)); + crypto::HashType hash{}; + if (txResult->status() == 0) + { + hash = tx->hash(); + } + Json::Value result = hash.hexPrefixed(); + buildJsonContent(result, response); co_return; } task::Task EthEndpoint::call(const Json::Value& request, Json::Value& response) { // params: transaction(TX), blockNumber(QTY|TAG) // result: data(DATA) - // FIXME: impl this + // TODO: impl this co_return; } task::Task EthEndpoint::estimateGas(const Json::Value& request, Json::Value& response) @@ -332,6 +368,7 @@ task::Task EthEndpoint::getLogs(const Json::Value&, Json::Value&) co_return; } +// return (actual block number, isLatest block) task::Task> EthEndpoint::getBlockNumberByTag( std::string_view blockTag) { diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp index cd30e9ffdc..0b8046a075 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp @@ -65,7 +65,7 @@ bcos::bytes bcos::Web3Transaction::encode() const codec::rlp::encode(out, nonce); if (type != TransactionType::EIP2930) { - codec::rlp::encode(out, maxFeePerGas); + codec::rlp::encode(out, maxPriorityFeePerGas); } // for EIP2930 it means gasPrice; for EIP1559 and EIP4844, it means max priority fee per gas codec::rlp::encode(out, maxFeePerGas); @@ -89,6 +89,42 @@ bcos::crypto::HashType bcos::Web3Transaction::hash() const codec::rlp::encode(encoded, *this); return bcos::crypto::keccak256Hash(bcos::ref(encoded)); } + +bcostars::Transaction Web3Transaction::toTarsTransaction() const +{ + bcostars::Transaction tarsTx{}; + tarsTx.data.nonce = std::to_string(this->nonce); + tarsTx.data.to = this->to.hexPrefixed(); + tarsTx.data.input.insert(tarsTx.data.input.end(), this->data.begin(), this->data.end()); + tarsTx.data.value = std::to_string(this->value); + tarsTx.data.gasLimit = this->gasLimit; + if (static_cast(this->type) >= static_cast(TransactionType::EIP1559)) + { + tarsTx.data.maxFeePerGas = std::to_string(this->maxFeePerGas); + tarsTx.data.maxPriorityFeePerGas = std::to_string(this->maxPriorityFeePerGas); + } + else + { + tarsTx.data.gasPrice = std::to_string(this->maxPriorityFeePerGas); + } + auto hash = this->hash(); + auto encodedForSign = this->encode(); + tarsTx.dataHash.insert(tarsTx.dataHash.end(), hash.begin(), hash.end()); + // FISCO BCOS signature is r||s||v + tarsTx.signature.insert( + tarsTx.signature.end(), this->signatureR.begin(), this->signatureR.end()); + tarsTx.signature.insert( + tarsTx.signature.end(), this->signatureS.begin(), this->signatureS.end()); + tarsTx.signature.push_back(static_cast(this->signatureV)); + + tarsTx.type = 1; + + tarsTx.extraTransactionBytes.insert( + tarsTx.extraTransactionBytes.end(), encodedForSign.begin(), encodedForSign.end()); + + return tarsTx; +} + namespace codec::rlp { Header header(const AccessListEntry& entry) noexcept diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h index 87ed7f3132..584b7adee2 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h @@ -24,7 +24,9 @@ #include #include #include +#include #include +#include namespace bcos { @@ -61,7 +63,7 @@ class Web3Transaction bcos::bytes encode() const; bcos::crypto::HashType hash() const; - + bcostars::Transaction toTarsTransaction() const; uint64_t getSignatureV() const { // EIP-155: Simple replay attack protection @@ -72,6 +74,22 @@ class Web3Transaction return signatureV + 27; } + std::string toString() const noexcept + { + std::stringstream stringstream{}; + stringstream << "chainId: " << this->chainId.value() + << " type: " << static_cast(this->type) << " to: " << this->to + << " data: " << this->data << " value: " << this->value + << " nonce: " << this->nonce << " gasLimit: " << this->gasLimit + << " maxPriorityFeePerGas: " << this->maxPriorityFeePerGas + << " maxFeePerGas: " << this->maxFeePerGas + << " maxFeePerBlobGas: " << this->maxFeePerBlobGas + << " blobVersionedHashes: " << this->blobVersionedHashes + << " signatureR: " << this->signatureR << " signatureS: " << this->signatureS + << " signatureV: " << this->signatureV; + return stringstream.str(); + } + std::optional chainId{std::nullopt}; // nullopt means a pre-EIP-155 transaction TransactionType type{TransactionType::Legacy}; Address to{}; diff --git a/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp b/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp index 4d72710952..686b9a8232 100644 --- a/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp +++ b/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp @@ -179,5 +179,152 @@ BOOST_AUTO_TEST_CASE(testEIP4844Transaction) auto rawTx2 = toHexStringWithPrefix(encoded); BOOST_CHECK_EQUAL(rawTx, rawTx2); } + +BOOST_AUTO_TEST_CASE(recoverAddress) +{ + // clang-format off + constexpr std::string_view rawTx = "0xf8ac82017c8504a817c800835fefd89409d07ecb4d6f32e91503c04b192e3bdeb7f857f480b8442c7128d700000000000000000000000000000000000000000000000000009bc24e89949a00000000000000000000000000000000000000000000000000000000000000001ba0cd372eb41b6b4e9e576233bb29c1492e0329fac1331f492a69e4a1b586a1a28ba032950cc4184ca8b0d45b24d13345157b4153d7ccc0d187dbab018be07726d186"; + // clang-format on + auto bytes = fromHexWithPrefix(rawTx); + auto bRef = bcos::ref(bytes); + Web3Transaction tx{}; + auto e = codec::rlp::decode(bRef, tx); + BOOST_CHECK(!e); + BOOST_CHECK(tx.type == TransactionType::Legacy); + bcos::bytes encoded{}; + codec::rlp::encode(encoded, tx); + auto rawTx2 = toHexStringWithPrefix(encoded); + BOOST_CHECK_EQUAL(rawTx, rawTx2); + + auto encodeForSign = tx.encode(); + bcos::bytes sign{}; + sign.insert(sign.end(), tx.signatureR.begin(), tx.signatureR.end()); + sign.insert(sign.end(), tx.signatureS.begin(), tx.signatureS.end()); + sign.push_back(tx.signatureV); + auto hashImpl = std::make_shared(); + auto hash = bcos::crypto::keccak256Hash(ref(encodeForSign)); + auto signatureImpl = std::make_shared(); + auto [re, addr] = signatureImpl->recoverAddress(*hashImpl, hash, ref(sign)); + BOOST_CHECK(re); + auto address = toHexStringWithPrefix(addr); + BOOST_CHECK_EQUAL(address, "0xec5e7dec9d2d6bfa1f2221ace01ae3deb6906fb0"); +} + +BOOST_AUTO_TEST_CASE(EIP1559Recover) +{ + // https://etherscan.io/tx/0xcf6b53ec88659fc86e854af2e8453fa519ca261f949ef291e33c5f44ead870dc + // clang-format off + constexpr std::string_view rawTx = "0x02f8720183015b148085089a36ae8682520894e10f39a0dfb9e380b6d176eb7183af32b68028d78806e9ba3bd88b600080c080a032ab966d1c9cc2be6952713a1599a95a14f6e92c9f62d7fa40aa62d8b764ffcfa060bdbe7b8e66a0c681a90d4da0c7c0a4ba9321d49fc5c65bfddb847539e35d56"; + // clang-format on + auto bytes = fromHexWithPrefix(rawTx); + auto bRef = bcos::ref(bytes); + Web3Transaction tx{}; + auto e = codec::rlp::decode(bRef, tx); + BOOST_CHECK(e == nullptr); + BOOST_CHECK(tx.type == TransactionType::EIP1559); + BOOST_CHECK(tx.chainId.has_value()); + BOOST_CHECK_EQUAL(tx.chainId.value(), 1); + BOOST_CHECK_EQUAL(tx.nonce, 88852); + BOOST_CHECK_EQUAL(tx.maxPriorityFeePerGas, 0); + BOOST_CHECK_EQUAL(tx.maxFeePerGas, 36947013254u); + BOOST_CHECK_EQUAL(tx.gasLimit, 21000); + BOOST_CHECK_EQUAL(tx.to.hexPrefixed(), "0xe10f39a0dfb9e380b6d176eb7183af32b68028d7"); + BOOST_CHECK_EQUAL(tx.value, 498134000000000000ull); + BOOST_CHECK_EQUAL(toHex(tx.data), ""); + BOOST_CHECK_EQUAL(tx.getSignatureV(), tx.chainId.value() * 2 + 35); + BOOST_CHECK_EQUAL(tx.hash().hexPrefixed(), + "0xcf6b53ec88659fc86e854af2e8453fa519ca261f949ef291e33c5f44ead870dc"); + bcos::bytes encoded{}; + codec::rlp::encode(encoded, tx); + auto rawTx2 = toHexStringWithPrefix(encoded); + BOOST_CHECK_EQUAL(rawTx, rawTx2); + + auto encodeForSign = tx.encode(); + bcos::bytes sign{}; + sign.insert(sign.end(), tx.signatureR.begin(), tx.signatureR.end()); + sign.insert(sign.end(), tx.signatureS.begin(), tx.signatureS.end()); + sign.push_back(tx.signatureV); + auto hashImpl = std::make_shared(); + auto hash = bcos::crypto::keccak256Hash(ref(encodeForSign)); + auto signatureImpl = std::make_shared(); + auto [re, addr] = signatureImpl->recoverAddress(*hashImpl, hash, ref(sign)); + BOOST_CHECK(re); + auto address = toHexStringWithPrefix(addr); + BOOST_CHECK_EQUAL(address, "0x595063172c85b1e8ac2fe74fcb6b7dc26844cc2d"); +} + +BOOST_AUTO_TEST_CASE(EIP4844Recover) +{ + // https://etherscan.io/tx/0x8bb97c1480b533396b0940a0f94ef5974c4989954f52d928e06e38d363bbd560 + // clang-format off + constexpr std::string_view rawTx = "0x03f9049f0183082ef8843b9aca008537942bfdb083036a2b941c479675ad559dc151f6ec7ed3fbf8cee79582b680b8a43e5aa082000000000000000000000000000000000000000000000000000000000008f7060000000000000000000000000000000000000000000000000000000000168763000000000000000000000000e64a54e2533fd126c2e452c5fab544d80e2e4eb5000000000000000000000000000000000000000000000000000000000a8cc7c7000000000000000000000000000000000000000000000000000000000a8ccabef902c0f8dd941c479675ad559dc151f6ec7ed3fbf8cee79582b6f8c6a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000aa0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca0a10aa54071443520884ed767b0684edf43acec528b7da83ab38ce60126562660f90141948315177ab297ba92a06054ce80a67ed4dbd7ed3af90129a00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000007a00000000000000000000000000000000000000000000000000000000000000009a0000000000000000000000000000000000000000000000000000000000000000aa0b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103a0360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca0a66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a873fbd8da0a66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a873fbd8ea0f652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f379294a1f89b94e64a54e2533fd126c2e452c5fab544d80e2e4eb5f884a00000000000000000000000000000000000000000000000000000000000000004a00000000000000000000000000000000000000000000000000000000000000005a0e85fd79f89ff278fc57d40aecb7947873df9f0beac531c8f71a98f630e1eab62a07686888b19bb7b75e46bb1aa328b65150743f4899443d722f0adf8e252ccda410af8c6a001f8198b33db3461035e1621dd12498e57cf26efe9578b39054fbe5efdf83032a00152295a881b358db5dcf58b54661ee60f595de7f57eb93030a5d9e57bcae30ea0014ea3a3d4fc547ccb6974c5c4deb7778b755b0b3d56be88c54ef3a39d209b4ca001b378a4a2a4a3806740ec38b5672d213c78bbcae34550d014a265fc262fe06ea001b83eca80127748b71bcaa6a8c9edbfd5a9fb47933032891c27e07668f48867a001904e6186ecd84f6897659777846d5510bfbeb2863a93d8432f0fcf89c3e2c901a028bc2660c742d25de1f9af5550bfb734ac81c1e0d703c285447684872430635aa01788719406012ded6dd859a3a0218cb0acccd3f30a93da6796abc19ba3192fcf"; + // clang-format on + auto bytes = fromHexWithPrefix(rawTx); + auto bRef = bcos::ref(bytes); + Web3Transaction tx{}; + auto e = codec::rlp::decode(bRef, tx); + BOOST_CHECK(e == nullptr); + BOOST_CHECK(tx.type == TransactionType::EIP4844); + BOOST_CHECK(tx.chainId.has_value()); + BOOST_CHECK_EQUAL(tx.chainId.value(), 1); + BOOST_CHECK_EQUAL(tx.nonce, 536312); + BOOST_CHECK_EQUAL(tx.maxPriorityFeePerGas, 1000000000); + BOOST_CHECK_EQUAL(tx.maxFeePerGas, 238709112240); + BOOST_CHECK_EQUAL(tx.gasLimit, 223787); + BOOST_CHECK_EQUAL(tx.to.hexPrefixed(), "0x1c479675ad559dc151f6ec7ed3fbf8cee79582b6"); + BOOST_CHECK_EQUAL(tx.value, 0); + // clang-format off + BOOST_CHECK_EQUAL(toHex(tx.data), "3e5aa082000000000000000000000000000000000000000000000000000000000008f7060000000000000000000000000000000000000000000000000000000000168763000000000000000000000000e64a54e2533fd126c2e452c5fab544d80e2e4eb5000000000000000000000000000000000000000000000000000000000a8cc7c7000000000000000000000000000000000000000000000000000000000a8ccabe"); + // clang-format on + BOOST_CHECK_EQUAL(tx.getSignatureV(), tx.chainId.value() * 2 + 36); + BOOST_CHECK_EQUAL(tx.hash().hexPrefixed(), + "0x8bb97c1480b533396b0940a0f94ef5974c4989954f52d928e06e38d363bbd560"); + + BOOST_CHECK_EQUAL(tx.blobVersionedHashes.size(), 6); + BOOST_CHECK_EQUAL(toHex(tx.blobVersionedHashes[0]), + "01f8198b33db3461035e1621dd12498e57cf26efe9578b39054fbe5efdf83032"); + BOOST_CHECK_EQUAL(toHex(tx.blobVersionedHashes[1]), + "0152295a881b358db5dcf58b54661ee60f595de7f57eb93030a5d9e57bcae30e"); + BOOST_CHECK_EQUAL(toHex(tx.blobVersionedHashes[2]), + "014ea3a3d4fc547ccb6974c5c4deb7778b755b0b3d56be88c54ef3a39d209b4c"); + BOOST_CHECK_EQUAL(toHex(tx.blobVersionedHashes[3]), + "01b378a4a2a4a3806740ec38b5672d213c78bbcae34550d014a265fc262fe06e"); + BOOST_CHECK_EQUAL(toHex(tx.blobVersionedHashes[4]), + "01b83eca80127748b71bcaa6a8c9edbfd5a9fb47933032891c27e07668f48867"); + BOOST_CHECK_EQUAL(toHex(tx.blobVersionedHashes[5]), + "01904e6186ecd84f6897659777846d5510bfbeb2863a93d8432f0fcf89c3e2c9"); + + BOOST_CHECK_EQUAL(tx.accessList.size(), 3); + BOOST_CHECK_EQUAL( + tx.accessList[0].account.hexPrefixed(), "0x1c479675ad559dc151f6ec7ed3fbf8cee79582b6"); + BOOST_CHECK_EQUAL(tx.accessList[0].storageKeys.size(), 6); + BOOST_CHECK_EQUAL(toHex(tx.accessList[0].storageKeys[5]), + "a10aa54071443520884ed767b0684edf43acec528b7da83ab38ce60126562660"); + BOOST_CHECK_EQUAL( + tx.accessList[1].account.hexPrefixed(), "0x8315177ab297ba92a06054ce80a67ed4dbd7ed3a"); + BOOST_CHECK_EQUAL(tx.accessList[1].storageKeys.size(), 9); + BOOST_CHECK_EQUAL( + tx.accessList[2].account.hexPrefixed(), "0xe64a54e2533fd126c2e452c5fab544d80e2e4eb5"); + BOOST_CHECK_EQUAL(tx.accessList[2].storageKeys.size(), 4); + + bcos::bytes encoded{}; + codec::rlp::encode(encoded, tx); + auto rawTx2 = toHexStringWithPrefix(encoded); + BOOST_CHECK_EQUAL(rawTx, rawTx2); + + auto encodeForSign = tx.encode(); + bcos::bytes sign{}; + sign.insert(sign.end(), tx.signatureR.begin(), tx.signatureR.end()); + sign.insert(sign.end(), tx.signatureS.begin(), tx.signatureS.end()); + sign.push_back(tx.signatureV); + auto hashImpl = std::make_shared(); + auto hash = bcos::crypto::keccak256Hash(ref(encodeForSign)); + auto signatureImpl = std::make_shared(); + auto [re, addr] = signatureImpl->recoverAddress(*hashImpl, hash, ref(sign)); + BOOST_CHECK(re); + auto address = toHexStringWithPrefix(addr); + BOOST_CHECK_EQUAL(address, "0xc1b634853cb333d3ad8663715b08f41a3aec47cc"); +} + BOOST_AUTO_TEST_SUITE_END() } // namespace bcos::test \ No newline at end of file diff --git a/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionFactoryImpl.h b/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionFactoryImpl.h index 601abd6907..f63b3aee4d 100644 --- a/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionFactoryImpl.h +++ b/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionFactoryImpl.h @@ -71,25 +71,30 @@ class TransactionFactoryImpl : public bcos::protocol::TransactionFactory } } - auto originDataHash = std::move(transaction->mutableInner().dataHash); - transaction->mutableInner().dataHash.clear(); - transaction->calculateHash(*m_cryptoSuite->hashImpl()); - - // check if hash matching - if (checkHash && !originDataHash.empty() && - (originDataHash != transaction->mutableInner().dataHash)) [[unlikely]] + // other transaction type, do not need to check hash, skip + if (transaction->type() == + static_cast(bcos::protocol::TransactionType::BCOSTransaction)) { - bcos::crypto::HashType originHashResult( - (bcos::byte*)originDataHash.data(), originDataHash.size()); - bcos::crypto::HashType hashResult( - (bcos::byte*)transaction->mutableInner().dataHash.data(), - transaction->mutableInner().dataHash.size()); - - BCOS_LOG(WARNING) << LOG_DESC("the transaction hash does not match") - << LOG_KV("originHash", originHashResult.hex()) - << LOG_KV("realHash", hashResult.hex()); - BOOST_THROW_EXCEPTION(std::invalid_argument( - "transaction hash mismatching, maybe transaction version not support.")); + auto originDataHash = std::move(transaction->mutableInner().dataHash); + transaction->mutableInner().dataHash.clear(); + transaction->calculateHash(*m_cryptoSuite->hashImpl()); + + // check if hash matching + if (checkHash && !originDataHash.empty() && + (originDataHash != transaction->mutableInner().dataHash)) [[unlikely]] + { + bcos::crypto::HashType originHashResult( + (bcos::byte*)originDataHash.data(), originDataHash.size()); + bcos::crypto::HashType hashResult( + (bcos::byte*)transaction->mutableInner().dataHash.data(), + transaction->mutableInner().dataHash.size()); + + BCOS_LOG(WARNING) << LOG_DESC("the transaction hash does not match") + << LOG_KV("originHash", originHashResult.hex()) + << LOG_KV("realHash", hashResult.hex()); + BOOST_THROW_EXCEPTION(std::invalid_argument( + "transaction hash mismatching, maybe transaction version not support.")); + } } if (checkSig) diff --git a/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionImpl.cpp b/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionImpl.cpp index e012f6f932..f105cff854 100644 --- a/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionImpl.cpp +++ b/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionImpl.cpp @@ -60,6 +60,7 @@ bcos::crypto::HashType TransactionImpl::hash() const void bcostars::protocol::TransactionImpl::calculateHash(const bcos::crypto::Hash& hashImpl) { + // TODO: based on type to switch bcos::concepts::hash::calculate(*m_inner(), hashImpl.hasher(), m_inner()->dataHash); } @@ -174,6 +175,16 @@ void bcostars::protocol::TransactionImpl::setExtraData(std::string const& _extra { m_inner()->extraData = _extraData; } +uint8_t bcostars::protocol::TransactionImpl::type() const +{ + return static_cast(m_inner()->type); +} +bcos::bytesConstRef TransactionImpl::extraTransactionBytes() const +{ + return {reinterpret_cast(m_inner()->extraTransactionBytes.data()), + m_inner()->extraTransactionBytes.size()}; +} + const bcostars::Transaction& bcostars::protocol::TransactionImpl::inner() const { return *m_inner(); diff --git a/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionImpl.h b/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionImpl.h index 676985f0d8..1b1ee12e6c 100644 --- a/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionImpl.h +++ b/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionImpl.h @@ -93,6 +93,9 @@ class TransactionImpl : public bcos::protocol::Transaction std::string_view extraData() const override; void setExtraData(std::string const& _extraData) override; + uint8_t type() const override; + bcos::bytesConstRef extraTransactionBytes() const override; + const bcostars::Transaction& inner() const; bcostars::Transaction& mutableInner(); void setInner(bcostars::Transaction inner); diff --git a/bcos-txpool/bcos-txpool/TxPool.cpp b/bcos-txpool/bcos-txpool/TxPool.cpp index 75a1718171..b550e4d7f9 100644 --- a/bcos-txpool/bcos-txpool/TxPool.cpp +++ b/bcos-txpool/bcos-txpool/TxPool.cpp @@ -115,6 +115,12 @@ task::Task TxPool::submitTransaction( co_return co_await m_txpoolStorage->submitTransaction(std::move(transaction)); } +task::Task TxPool::submitTransactionWithoutReceipt( + protocol::Transaction::Ptr transaction) +{ + co_return co_await m_txpoolStorage->submitTransactionWithoutReceipt(std::move(transaction)); +} + task::Task TxPool::submitTransactionWithHook( protocol::Transaction::Ptr transaction, std::function onTxSubmitted) { diff --git a/bcos-txpool/bcos-txpool/TxPool.h b/bcos-txpool/bcos-txpool/TxPool.h index dc38b55769..6a92eff7f3 100644 --- a/bcos-txpool/bcos-txpool/TxPool.h +++ b/bcos-txpool/bcos-txpool/TxPool.h @@ -47,6 +47,9 @@ class TxPool : public TxPoolInterface, public std::enable_shared_from_this submitTransaction( protocol::Transaction::Ptr transaction) override; + task::Task submitTransactionWithoutReceipt( + protocol::Transaction::Ptr transaction) override; + task::Task submitTransactionWithHook( protocol::Transaction::Ptr transaction, std::function onTxSubmitted) override; diff --git a/bcos-txpool/bcos-txpool/txpool/interfaces/TxPoolStorageInterface.h b/bcos-txpool/bcos-txpool/txpool/interfaces/TxPoolStorageInterface.h index a9866985a5..b062e996d8 100644 --- a/bcos-txpool/bcos-txpool/txpool/interfaces/TxPoolStorageInterface.h +++ b/bcos-txpool/bcos-txpool/txpool/interfaces/TxPoolStorageInterface.h @@ -39,6 +39,8 @@ class TxPoolStorageInterface virtual task::Task submitTransaction( protocol::Transaction::Ptr transaction) = 0; + virtual task::Task submitTransactionWithoutReceipt( + protocol::Transaction::Ptr transaction) = 0; virtual std::vector getTransactions( RANGES::any_view hashes) = 0; diff --git a/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.cpp b/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.cpp index bbb81f45a1..6c46347299 100644 --- a/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.cpp +++ b/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.cpp @@ -20,6 +20,8 @@ */ #include "bcos-txpool/txpool/storage/MemoryStorage.h" #include "bcos-utilities/Common.h" + +#include #include #include #include @@ -171,6 +173,69 @@ task::Task MemoryStorage::submitTransact co_return co_await awaitable; } +task::Task MemoryStorage::submitTransactionWithoutReceipt( + protocol::Transaction::Ptr transaction) +{ + transaction->setImportTime(utcTime()); + struct Awaitable + { + [[maybe_unused]] constexpr bool await_ready() { return false; } + [[maybe_unused]] void await_suspend(CO_STD::coroutine_handle<> handle) + { + try + { + auto result = m_self->verifyAndSubmitTransaction( + std::move(m_transaction), nullptr, true, true); + + if (result != TransactionStatus::None) + { + TXPOOL_LOG(DEBUG) + << "Submit transaction failed! " + << LOG_KV("TxHash", m_transaction ? m_transaction->hash().hex() : "") + << LOG_KV("result", result); + m_submitResult.emplace( + BCOS_ERROR_PTR((int32_t)result, bcos::protocol::toString(result))); + } + else + { + auto res = std::make_shared(); + res->setStatus(static_cast(result)); + m_submitResult.emplace(std::move(res)); + } + handle.resume(); + } + catch (std::exception& e) + { + TXPOOL_LOG(WARNING) << "Unexpected exception: " << boost::diagnostic_information(e); + m_submitResult.emplace( + BCOS_ERROR_PTR((int32_t)TransactionStatus::Malformed, "Unknown exception")); + handle.resume(); + } + } + bcos::protocol::TransactionSubmitResult::Ptr await_resume() + { + if (std::holds_alternative(m_submitResult)) + { + BOOST_THROW_EXCEPTION(*std::get(m_submitResult)); + } + + return std::move( + std::get(m_submitResult)); + } + + protocol::Transaction::Ptr m_transaction; + std::shared_ptr m_self; + std::variant + m_submitResult; + }; + + Awaitable awaitable{.m_transaction = std::move(transaction), + .m_self = shared_from_this(), + .m_submitResult = {}}; + co_return co_await awaitable; +} + + std::vector MemoryStorage::getTransactions( RANGES::any_view hashes) { diff --git a/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.h b/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.h index c6bdcb928b..e4ba9e2441 100644 --- a/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.h +++ b/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.h @@ -64,6 +64,9 @@ class MemoryStorage : public TxPoolStorageInterface, task::Task submitTransaction( protocol::Transaction::Ptr transaction) override; + task::Task submitTransactionWithoutReceipt( + protocol::Transaction::Ptr transaction) override; + std::vector getTransactions( RANGES::any_view hashes) override; diff --git a/bcos-txpool/bcos-txpool/txpool/validator/LedgerNonceChecker.cpp b/bcos-txpool/bcos-txpool/txpool/validator/LedgerNonceChecker.cpp index 8457246951..5612236433 100644 --- a/bcos-txpool/bcos-txpool/txpool/validator/LedgerNonceChecker.cpp +++ b/bcos-txpool/bcos-txpool/txpool/validator/LedgerNonceChecker.cpp @@ -41,7 +41,7 @@ TransactionStatus LedgerNonceChecker::checkNonce(Transaction::ConstPtr _tx, bool { return status; } - if (m_checkBlockLimit) + if (m_checkBlockLimit && _tx->type() == static_cast(TransactionType::BCOSTransaction)) { // check blockLimit return checkBlockLimit(_tx); } diff --git a/bcos-txpool/bcos-txpool/txpool/validator/TxValidator.cpp b/bcos-txpool/bcos-txpool/txpool/validator/TxValidator.cpp index 1a8cd1f246..03e7226562 100644 --- a/bcos-txpool/bcos-txpool/txpool/validator/TxValidator.cpp +++ b/bcos-txpool/bcos-txpool/txpool/validator/TxValidator.cpp @@ -31,11 +31,13 @@ TransactionStatus TxValidator::verify(bcos::protocol::Transaction::ConstPtr _tx) return TransactionStatus::InvalidSignature; } // check groupId and chainId - if (_tx->groupId() != m_groupId) [[unlikely]] + if (_tx->groupId() != m_groupId && + _tx->type() == static_cast(TransactionType::BCOSTransaction)) [[unlikely]] { return TransactionStatus::InvalidGroupId; } - if (_tx->chainId() != m_chainId) [[unlikely]] + if (_tx->chainId() != m_chainId && + _tx->type() == static_cast(TransactionType::BCOSTransaction)) [[unlikely]] { return TransactionStatus::InvalidChainId; } diff --git a/cmake/CompilerSettings.cmake b/cmake/CompilerSettings.cmake index 7ec4c416a7..4b5026a99e 100644 --- a/cmake/CompilerSettings.cmake +++ b/cmake/CompilerSettings.cmake @@ -76,7 +76,7 @@ if(("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR("${CMAKE_CXX_COMPILER_ID}" MATC endif() # Configuration-specific compiler settings. - set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") + set(CMAKE_CXX_FLAGS_DEBUG "-Og -g") set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") if(ONLY_CPP_SDK) set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") From fb97374a8b6bafbb9de95ef98c56b946e9ff23d0 Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Mon, 15 Apr 2024 13:57:31 +0800 Subject: [PATCH 11/39] (rpc): impl eth_call eth_getTransactionReceipt, logs bloom calculate. (#4365) --- bcos-framework/bcos-framework/ledger/Ledger.h | 18 +++ .../bcos-framework/protocol/LogEntry.h | 3 + .../protocol/TransactionReceipt.h | 2 + bcos-ledger/src/libledger/LedgerMethods.cpp | 81 +++++++++++++ bcos-ledger/src/libledger/LedgerMethods.h | 7 ++ .../bcos-rpc/jsonrpc/JsonRpcInterface.cpp | 5 +- .../web3jsonrpc/endpoints/EthEndpoint.cpp | 97 +++++++++++++++- .../web3jsonrpc/model/BlockResponse.cpp | 21 ++++ .../web3jsonrpc/model/BlockResponse.h | 32 ++++++ bcos-rpc/bcos-rpc/web3jsonrpc/model/Bloom.cpp | 54 +++++++++ bcos-rpc/bcos-rpc/web3jsonrpc/model/Bloom.h | 44 ++++++++ .../web3jsonrpc/model/CallRequest.cpp | 74 ++++++++++++ .../bcos-rpc/web3jsonrpc/model/CallRequest.h | 64 +++++++++++ .../web3jsonrpc/model/FilterRequest.cpp | 21 ++++ .../web3jsonrpc/model/FilterRequest.h | 34 ++++++ bcos-rpc/bcos-rpc/web3jsonrpc/model/Log.h | 68 +++++++++++ .../web3jsonrpc/model/ReceiptResponse.h | 106 ++++++++++++++++++ .../web3jsonrpc/model/Web3Transaction.cpp | 8 +- .../web3jsonrpc/model/Web3Transaction.h | 39 ++++--- bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp | 14 +-- .../protocol/TransactionReceiptImpl.h | 1 + 21 files changed, 762 insertions(+), 31 deletions(-) create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.cpp create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.h create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/model/Bloom.cpp create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/model/Bloom.h create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/model/CallRequest.cpp create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/model/CallRequest.h create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/model/FilterRequest.cpp create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/model/FilterRequest.h create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/model/Log.h create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h diff --git a/bcos-framework/bcos-framework/ledger/Ledger.h b/bcos-framework/bcos-framework/ledger/Ledger.h index 07e3974f5c..a84c95ad31 100644 --- a/bcos-framework/bcos-framework/ledger/Ledger.h +++ b/bcos-framework/bcos-framework/ledger/Ledger.h @@ -122,6 +122,24 @@ inline constexpr struct GetFeatures } } getFeatures{}; +inline constexpr struct GetReceipt +{ + task::Task operator()( + auto& ledger, crypto::HashType hash) const + { + co_return co_await tag_invoke(*this, ledger, hash); + } +} getReceipt{}; + +inline constexpr struct GetTransactions +{ + task::Task operator()( + auto& ledger, crypto::HashListPtr hashes) const + { + co_return co_await tag_invoke(*this, ledger, std::move(hashes)); + } +} getTransactions{}; + template using tag_t = std::decay_t; } // namespace bcos::ledger diff --git a/bcos-framework/bcos-framework/protocol/LogEntry.h b/bcos-framework/bcos-framework/protocol/LogEntry.h index d6dcd2c4a0..b8aa9f1133 100644 --- a/bcos-framework/bcos-framework/protocol/LogEntry.h +++ b/bcos-framework/bcos-framework/protocol/LogEntry.h @@ -41,6 +41,9 @@ class LogEntry std::string_view address() const { return {(const char*)m_address.data(), m_address.size()}; } gsl::span topics() const { return {m_topics.data(), m_topics.size()}; } bcos::bytesConstRef data() const { return ref(m_data); } + bytes&& takeAddress() { return std::move(m_address); } + h256s&& takeTopics() { return std::move(m_topics); } + bytes&& takeData() { return std::move(m_data); } // Define the scale decode method, which cannot be modified at will template > friend Stream& operator>>(Stream& _stream, LogEntry& _logEntry) diff --git a/bcos-framework/bcos-framework/protocol/TransactionReceipt.h b/bcos-framework/bcos-framework/protocol/TransactionReceipt.h index d53222ef67..48e92493de 100644 --- a/bcos-framework/bcos-framework/protocol/TransactionReceipt.h +++ b/bcos-framework/bcos-framework/protocol/TransactionReceipt.h @@ -18,6 +18,7 @@ */ #pragma once +#include "LogEntry.h" #include "ProtocolTypeDef.h" #include #include @@ -46,6 +47,7 @@ class TransactionReceipt virtual int32_t status() const = 0; virtual bcos::bytesConstRef output() const = 0; virtual gsl::span logEntries() const = 0; + virtual LogEntries&& takeLogEntris() = 0; virtual protocol::BlockNumber blockNumber() const = 0; virtual std::string_view effectiveGasPrice() const = 0; virtual void setEffectiveGasPrice(std::string effectiveGasPrice) = 0; diff --git a/bcos-ledger/src/libledger/LedgerMethods.cpp b/bcos-ledger/src/libledger/LedgerMethods.cpp index 03833538ff..ff70af8bfd 100644 --- a/bcos-ledger/src/libledger/LedgerMethods.cpp +++ b/bcos-ledger/src/libledger/LedgerMethods.cpp @@ -445,3 +445,84 @@ bcos::task::Task bcos::ledger::tag_invoke( co_return features; } + +bcos::task::Task bcos::ledger::tag_invoke( + ledger::tag_t, LedgerInterface& ledger, crypto::HashType const& txHash) +{ + struct Awaitable + { + bcos::ledger::LedgerInterface& m_ledger; + bcos::crypto::HashType m_hash; + + std::variant m_result; + + constexpr static bool await_ready() noexcept { return false; } + void await_suspend(CO_STD::coroutine_handle<> handle) + { + m_ledger.asyncGetTransactionReceiptByHash(m_hash, false, + [this, handle](bcos::Error::Ptr error, + bcos::protocol::TransactionReceipt::ConstPtr receipt, MerkleProofPtr) { + if (error) + { + m_result.emplace(std::move(error)); + } + else + { + m_result.emplace(receipt); + } + handle.resume(); + }); + } + bcos::protocol::TransactionReceipt::ConstPtr await_resume() + { + if (std::holds_alternative(m_result)) + { + BOOST_THROW_EXCEPTION(*std::get(m_result)); + } + return std::get(m_result); + } + }; + + Awaitable awaitable{.m_ledger = ledger, .m_hash = std::move(txHash), .m_result = {}}; + co_return co_await awaitable; +} + +bcos::task::Task bcos::ledger::tag_invoke( + ledger::tag_t, LedgerInterface& ledger, crypto::HashListPtr hashes) +{ + struct Awaitable + { + bcos::ledger::LedgerInterface& m_ledger; + bcos::crypto::HashListPtr m_hashes; + + std::variant m_result; + + constexpr static bool await_ready() noexcept { return false; } + void await_suspend(CO_STD::coroutine_handle<> handle) + { + m_ledger.asyncGetBatchTxsByHashList( + std::move(m_hashes), false, [this, handle](auto&& error, auto&& txs, auto&&) { + if (error) + { + m_result.emplace(std::move(error)); + } + else + { + m_result.emplace(txs); + } + handle.resume(); + }); + } + bcos::protocol::TransactionsConstPtr await_resume() + { + if (std::holds_alternative(m_result)) + { + BOOST_THROW_EXCEPTION(*std::get(m_result)); + } + return std::get(m_result); + } + }; + + Awaitable awaitable{.m_ledger = ledger, .m_hashes = std::move(hashes), .m_result = {}}; + co_return co_await awaitable; +} diff --git a/bcos-ledger/src/libledger/LedgerMethods.h b/bcos-ledger/src/libledger/LedgerMethods.h index b2c77cea53..7fe3a1a3e2 100644 --- a/bcos-ledger/src/libledger/LedgerMethods.h +++ b/bcos-ledger/src/libledger/LedgerMethods.h @@ -88,4 +88,11 @@ task::Task tag_invoke( ledger::tag_t /*unused*/, LedgerInterface& ledger); task::Task tag_invoke(ledger::tag_t /*unused*/, LedgerInterface& ledger); + +task::Task tag_invoke( + ledger::tag_t, LedgerInterface& ledger, crypto::HashType const& txHash); + +task::Task tag_invoke( + ledger::tag_t, LedgerInterface& ledger, crypto::HashListPtr hashes); + } // namespace bcos::ledger \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.cpp b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.cpp index 157a81799a..fdce34b859 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.cpp +++ b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.cpp @@ -225,7 +225,10 @@ void bcos::rpc::parseRpcRequestJson(std::string_view _requestBody, JsonRequest& bcos::bytes bcos::rpc::toStringResponse(JsonResponse _jsonResponse) { auto jResp = toJsonResponse(std::move(_jsonResponse)); - std::unique_ptr writer(Json::StreamWriterBuilder().newStreamWriter()); + auto builder = Json::StreamWriterBuilder(); + builder["commentStyle"] = "None"; + builder["indentation"] = ""; + std::unique_ptr writer(builder.newStreamWriter()); class JsonSink { public: diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp index 9b165c36bc..3ee294ed10 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include #include @@ -232,7 +234,72 @@ task::Task EthEndpoint::call(const Json::Value& request, Json::Value& resp { // params: transaction(TX), blockNumber(QTY|TAG) // result: data(DATA) - // TODO: impl this + auto scheduler = m_nodeService->scheduler(); + if (!scheduler) + { + BOOST_THROW_EXCEPTION( + JsonRpcException(JsonRpcError::InternalError, "Scheduler not available!")); + } + auto [valid, call] = decodeCallRequest(request[0u]); + if (!valid) + { + BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParams, "Invalid call request!")); + } + if (c_fileLogLevel == TRACE) + { + RPC_IMPL_LOG(TRACE) << LOG_DESC("eth_call") << call; + } + auto&& tx = call.takeToTransaction(m_nodeService->blockFactory()->transactionFactory()); + // TODO: ignore params blockNumber here, use it after historical data is available + + // MOVE it into a new file + struct Awaitable + { + bcos::scheduler::SchedulerInterface& m_scheduler; + bcos::protocol::Transaction::Ptr m_tx; + std::variant m_result{}; + constexpr static bool await_ready() noexcept { return false; } + void await_suspend(CO_STD::coroutine_handle<> handle) noexcept + { + m_scheduler.call(m_tx, [this, handle](Error::Ptr&& error, auto&& result) { + if (error) + { + m_result.emplace(std::move(error)); + } + else + { + m_result.emplace(std::move(result)); + } + handle.resume(); + }); + } + protocol::TransactionReceipt::Ptr await_resume() noexcept + { + if (std::holds_alternative(m_result)) + { + BOOST_THROW_EXCEPTION(*std::get(m_result)); + } + return std::move(std::get(m_result)); + } + }; + auto const result = co_await Awaitable{.m_scheduler = *scheduler, .m_tx = std::move(tx)}; + + Json::Value jsonResult; + auto output = toHexStringWithPrefix(result->output()); + if (result->status() == static_cast(protocol::TransactionStatus::None)) + { + jsonResult = std::move(output); + } + else + { + // https://docs.infura.io/api/networks/ethereum/json-rpc-methods/eth_call#returns + jsonResult = Json::objectValue; + jsonResult["code"] = result->status(); + jsonResult["message"] = result->message(); + jsonResult["data"] = std::move(output); + response["jsonrpc"] = "2.0"; + response["error"] = std::move(jsonResult); + } co_return; } task::Task EthEndpoint::estimateGas(const Json::Value& request, Json::Value& response) @@ -283,14 +350,32 @@ task::Task EthEndpoint::getTransactionByBlockNumberAndIndex(const Json::Va JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); co_return; } -task::Task EthEndpoint::getTransactionReceipt(const Json::Value&, Json::Value&) +task::Task EthEndpoint::getTransactionReceipt( + const Json::Value& request, Json::Value& response) { // params: transactionHash(DATA) // result: transactionReceipt(RECEIPT) - - BOOST_THROW_EXCEPTION( - JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); - + auto const hashStr = toView(request[0U]); + auto hash = crypto::HashType(hashStr, crypto::HashType::FromHex); + auto const ledger = m_nodeService->ledger(); + auto receipt = co_await ledger::getReceipt(*ledger, hash); + auto receiptResponse = ReceiptResponse(std::move(receipt)); + auto hashList = std::make_shared(); + hashList->push_back(hash); + auto txs = co_await ledger::getTransactions(*ledger, std::move(hashList)); + if (txs->size() == 1) + { + receiptResponse.updateTxInfo(*txs->at(0)); + } + auto block = co_await ledger::getBlockData(*ledger, receiptResponse.blockNumber, + bcos::ledger::HEADER & bcos::ledger::TRANSACTIONS_HASH); + if (block) [[likely]] + { + receiptResponse.updateBlockInfo(*block); + } + Json::Value result = Json::objectValue; + receiptResponse.toJson(result); + buildJsonContent(result, response); co_return; } task::Task EthEndpoint::getUncleByBlockHashAndIndex(const Json::Value&, Json::Value& response) diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.cpp new file mode 100644 index 0000000000..d51a6076a4 --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.cpp @@ -0,0 +1,21 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file BlockResponse.cpp + * @author: kyonGuo + * @date 2024/4/11 + */ + +#include "BlockResponse.h" diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.h new file mode 100644 index 0000000000..a7de6c20bc --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.h @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file BlockResponse.h + * @author: kyonGuo + * @date 2024/4/11 + */ + +#pragma once +#include +#include +#include +#include + +namespace bcos::rpc +{ +class BlockResponse +{ +}; +} // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Bloom.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Bloom.cpp new file mode 100644 index 0000000000..c3fa645894 --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Bloom.cpp @@ -0,0 +1,54 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file Bloom.cpp + * @author: kyonGuo + * @date 2024/4/12 + */ + +#include "Bloom.h" + +using namespace bcos; +using namespace bcos::rpc; + +void rpc::bytesToBloom(concepts::ByteBuffer auto const& _bytes, Bloom& _bloom) +{ + auto hash = crypto::keccak256Hash(RefDataContainer(_bytes.data(), _bytes.size())); + static_assert(sizeof(crypto::HashType) == 32); + static_assert(sizeof(bcos::byte) == 1); + static_assert(sizeof(unsigned short) == 2); + for (size_t i = 0; i < 6; i += 2) + { + // bitPosition = ((first byte) & 0x07 << 8) + (second byte) + const unsigned short bitPosition = + static_cast((hash[i] & LOWER_3_BITS) << 8) + hash[i + 1]; + const size_t positionInBytes = BloomBytesSize - 1 - bitPosition / 8; + _bloom[positionInBytes] |= 1 << (bitPosition % 8); + } +} + +Bloom rpc::getLogsBloom(Logs const& logs) +{ + Bloom bloom{}; + for (auto const& log : logs) + { + bytesToBloom(log.address, bloom); + for (auto& topic : log.topics) + { + bytesToBloom(topic, bloom); + } + } + return bloom; +} \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Bloom.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Bloom.h new file mode 100644 index 0000000000..1d40beb8b3 --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Bloom.h @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file Bloom.h + * @author: kyonGuo + * @date 2024/4/12 + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace bcos::rpc +{ +constexpr size_t BloomBytesSize = 256; +constexpr uint8_t LOWER_3_BITS = 0b00000111; +using Bloom = std::array; + +void bytesToBloom(bcos::concepts::ByteBuffer auto const& _bytes, Bloom& _bloom); + +Bloom getLogsBloom(Logs const& logs); + +inline std::string_view toStringView(Bloom const& bloom) +{ + return {reinterpret_cast(bloom.data()), bloom.size()}; +} + +} // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/CallRequest.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/model/CallRequest.cpp new file mode 100644 index 0000000000..977d94f09f --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/CallRequest.cpp @@ -0,0 +1,74 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file CallRequest.cpp + * @author: kyonGuo + * @date 2024/4/11 + */ + +#include "CallRequest.h" + +using namespace bcos; +using namespace bcos::rpc; + +bcos::protocol::Transaction::Ptr CallRequest::takeToTransaction( + bcos::protocol::TransactionFactory::Ptr const& factory) noexcept +{ + auto tx = factory->createTransaction(0, std::move(this->to), std::move(this->data), "", 0, {}, + {}, 0, "", value.value_or(""), gasPrice.value_or(""), gas.value_or(0), + maxFeePerGas.value_or(""), maxPriorityFeePerGas.value_or("")); + return tx; +} + + +std::tuple rpc::decodeCallRequest(Json::Value const& _root) noexcept +{ + CallRequest _request; + if (!_root.isObject()) + { + return {false, _request}; + } + if (!_root.isMember("to") || !_root.isMember("data")) + { + return {false, _request}; + } + _request.to = _root["to"].asString(); + _request.data = bcos::fromHexWithPrefix(_root["data"].asString()); + if (_root.isMember("from")) + { + _request.from = _root["from"].asString(); + } + if (_root.isMember("gas")) + { + _request.gas = fromQuantity(_root["gas"].asString()); + } + if (_root.isMember("gasPrice")) + { + _request.gasPrice = _root["gasPrice"].asString(); + } + if (_root.isMember("value")) + { + _request.value = _root["value"].asString(); + } + if (_root.isMember("maxPriorityFeePerGas")) + { + _request.maxPriorityFeePerGas = _root["maxPriorityFeePerGas"].asString(); + } + if (_root.isMember("maxFeePerGas")) + { + _request.maxFeePerGas = _root["maxFeePerGas"].asString(); + } + return {true, std::move(_request)}; +} \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/CallRequest.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/CallRequest.h new file mode 100644 index 0000000000..118fe667b0 --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/CallRequest.h @@ -0,0 +1,64 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file CallRequest.h + * @author: kyonGuo + * @date 2024/4/11 + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace bcos::rpc +{ +struct CallRequest +{ + // Address + std::optional from{}; + // Address + std::string to{}; + bcos::bytes data{}; + // Quantity + std::optional gas{}; + // Quantity + std::optional gasPrice{}; + // Quantity + std::optional value{}; + std::optional maxPriorityFeePerGas{}; + std::optional maxFeePerGas{}; + + + friend std::ostream& operator<<(std::ostream& _out, const CallRequest& _in) + { + _out << "from: " << _in.from.value_or("null") << ", "; + _out << "to: " << _in.to << ", "; + _out << "data: " << bcos::toHex(_in.data) << ", "; + _out << "gas: " << _in.gas.value_or(0) << ", "; + _out << "gasPrice: " << _in.gasPrice.value_or("") << ", "; + _out << "value: " << _in.value.value_or("") << ", "; + _out << "maxPriorityFeePerGas: " << _in.maxPriorityFeePerGas.value_or("") << ", "; + _out << "maxFeePerGas: " << _in.maxFeePerGas.value_or("") << ", "; + return _out; + } + bcos::protocol::Transaction::Ptr takeToTransaction( + bcos::protocol::TransactionFactory::Ptr const&) noexcept; +}; +[[maybe_unused]] std::tuple decodeCallRequest(Json::Value const& _root) noexcept; +} // namespace bcos::rpc diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/FilterRequest.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/model/FilterRequest.cpp new file mode 100644 index 0000000000..f5aa185ade --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/FilterRequest.cpp @@ -0,0 +1,21 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file FilterRequest.cpp + * @author: kyonGuo + * @date 2024/4/11 + */ + +#include "FilterRequest.h" diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/FilterRequest.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/FilterRequest.h new file mode 100644 index 0000000000..43673eb1ce --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/FilterRequest.h @@ -0,0 +1,34 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file FilterRequest.h + * @author: kyonGuo + * @date 2024/4/11 + */ + +#pragma once +#include +#include +#include +#include + +namespace bcos::rpc +{ + +class FilterRequest +{ +}; + +} // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Log.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Log.h new file mode 100644 index 0000000000..43e1af9414 --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Log.h @@ -0,0 +1,68 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file Log.h + * @author: kyonGuo + * @date 2024/4/11 + */ + +#pragma once + +#include +#include +#include + +namespace bcos::rpc +{ +struct Log +{ + bool removed = false; + bcos::bytes address{}; + h256s topics{}; + bcos::bytes data{}; + protocol::BlockNumber number{0}; + crypto::HashType blockHash{}; + crypto::HashType transactionHash{}; + uint32_t txIndex{0}; + uint32_t logIndex{0}; + + friend std::ostream& operator<<(std::ostream& _out, const Log& _in) + { + _out << "address: " << toHex(_in.address) << ", "; + _out << "topics: ["; + for (auto& topic : _in.topics) + { + _out << topic.hex() << ", "; + } + _out << "], "; + _out << "data: " << bcos::toHex(_in.data) << ", "; + _out << "number: " << _in.number << ", "; + _out << "blockHash: " << _in.blockHash.hex() << ", "; + _out << "transactionHash: " << _in.transactionHash.hex() << ", "; + _out << "txIndex: " << _in.txIndex << ", "; + _out << "logIndex: " << _in.logIndex << ", "; + return _out; + } +}; +using Logs = std::vector; +[[maybe_unused]] static std::ostream& operator<<(std::ostream& _out, const Logs& _in) +{ + for (const auto& log : _in) + { + _out << log; + } + return _out; +} +} // namespace bcos::rpc diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h new file mode 100644 index 0000000000..2e5e6bf321 --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h @@ -0,0 +1,106 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file ReceiptResponse.h + * @author: kyonGuo + * @date 2024/4/11 + */ + +#pragma once +#include "Bloom.h" + + +#include +#include +#include +#include +#include +#include + +#include + +namespace bcos::rpc +{ +struct ReceiptResponse +{ + ReceiptResponse() = default; + explicit ReceiptResponse(bcos::protocol::TransactionReceipt::ConstPtr&& receipt) + { + status = (receipt->status() == 0 ? 1 : 0); + txHash = receipt->hash(); + effectiveGasPrice = std::string(receipt->effectiveGasPrice()); + gasUsed = receipt->gasUsed(); + blockNumber = receipt->blockNumber(); + if (!receipt->contractAddress().empty()) + { + contractAddress = Address(std::string(receipt->contractAddress())); + } + output = receipt->output().toBytes(); + auto mutableReceipt = const_cast(receipt.get()); + auto receiptLog = mutableReceipt->takeLogEntris(); + this->logs.reserve(receiptLog.size()); + for (size_t i = 0; i < receiptLog.size(); i++) + { + rpc::Log log{.address = std::move(receiptLog[i].takeAddress()), + .topics = std::move(receiptLog[i].takeTopics()), + .data = std::move(receiptLog[i].takeData())}; + log.logIndex = i; + log.number = blockNumber; + this->logs.push_back(std::move(log)); + } + logsBloom = getLogsBloom(logs); + } + int32_t status{0}; + uint32_t txIndex{0}; + crypto::HashType txHash{}; + crypto::HashType blockHash{}; + // Quantity + protocol::BlockNumber blockNumber{0}; + std::string from{}; + std::string to{}; + u256 cumulativeGasUsed{0}; + std::string effectiveGasPrice{}; + u256 gasUsed{0}; + // only has value in create tx + std::optional
contractAddress{}; + TransactionType type = TransactionType::Legacy; + Logs logs{}; + + Bloom logsBloom{}; + bcos::bytes output{}; + + void updateTxInfo(bcos::protocol::Transaction const& tx) + { + // txHash = tx.hash(); + // from = std::string(tx.sender()); + // to = tx.to().empty() ? "" : std::string(tx.to()); + // if (!tx.extraTransactionBytes().empty()) + // { + // if (auto firstByte = tx.extraTransactionBytes()[0]; + // firstByte < bcos::codec::rlp::BYTES_HEAD_BASE) + // { + // type = static_cast(firstByte); + // } + // } + } + + // TODO: update block info + void updateBlockInfo(bcos::protocol::Block const& block) {} + + // TODO: to json + void toJson(Json::Value&) {} +}; + +} // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp index 0b8046a075..cd31d242f6 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp @@ -24,11 +24,13 @@ namespace bcos { +namespace rpc +{ using codec::rlp::decode; using codec::rlp::encode; using codec::rlp::header; using codec::rlp::length; -bcos::bytes bcos::Web3Transaction::encode() const +bcos::bytes Web3Transaction::encode() const { bcos::bytes out; if (type == TransactionType::Legacy) @@ -83,7 +85,7 @@ bcos::bytes bcos::Web3Transaction::encode() const return out; } -bcos::crypto::HashType bcos::Web3Transaction::hash() const +bcos::crypto::HashType Web3Transaction::hash() const { bcos::bytes encoded{}; codec::rlp::encode(encoded, *this); @@ -124,9 +126,11 @@ bcostars::Transaction Web3Transaction::toTarsTransaction() const return tarsTx; } +} // namespace rpc namespace codec::rlp { +using namespace bcos::rpc; Header header(const AccessListEntry& entry) noexcept { auto len = length(entry.storageKeys); diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h index 584b7adee2..5ddedbd767 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h @@ -29,7 +29,8 @@ #include namespace bcos { - +namespace rpc +{ // EIP-2718 transaction type // https://github.com/ethereum/eth1.0-specs/tree/master/lists/signature-types enum class TransactionType : uint8_t @@ -40,6 +41,12 @@ enum class TransactionType : uint8_t EIP4844 = 3, // https://eips.ethereum.org/EIPS/eip-4844 }; +[[maybe_unused]] static std::ostream& operator<<(std::ostream& _out, const TransactionType& _in) +{ + _out << magic_enum::enum_name(_in); + return _out; +} + // EIP-2930: Access lists struct AccessListEntry { @@ -77,15 +84,16 @@ class Web3Transaction std::string toString() const noexcept { std::stringstream stringstream{}; - stringstream << "chainId: " << this->chainId.value() + stringstream << "chainId: " << this->chainId.value_or(0) << " type: " << static_cast(this->type) << " to: " << this->to - << " data: " << this->data << " value: " << this->value + << " data: " << toHex(this->data) << " value: " << this->value << " nonce: " << this->nonce << " gasLimit: " << this->gasLimit << " maxPriorityFeePerGas: " << this->maxPriorityFeePerGas << " maxFeePerGas: " << this->maxFeePerGas << " maxFeePerBlobGas: " << this->maxFeePerBlobGas << " blobVersionedHashes: " << this->blobVersionedHashes - << " signatureR: " << this->signatureR << " signatureS: " << this->signatureS + << " signatureR: " << toHex(this->signatureR) + << " signatureS: " << toHex(this->signatureS) << " signatureV: " << this->signatureV; return stringstream.str(); } @@ -104,23 +112,24 @@ class Web3Transaction uint64_t maxFeePerGas{0}; // EIP-4844: Shard Blob Transactions uint64_t maxFeePerBlobGas{0}; - std::vector blobVersionedHashes{}; + h256s blobVersionedHashes{}; bcos::bytes signatureR{}; bcos::bytes signatureS{}; uint64_t signatureV{0}; }; +} // namespace rpc namespace codec::rlp { -Header header(const AccessListEntry& entry) noexcept; -void encode(bcos::bytes& out, const AccessListEntry&) noexcept; -size_t length(const AccessListEntry&) noexcept; +Header header(const rpc::AccessListEntry& entry) noexcept; +void encode(bcos::bytes& out, const rpc::AccessListEntry&) noexcept; +size_t length(const rpc::AccessListEntry&) noexcept; -size_t length(const Web3Transaction&) noexcept; -Header headerForSign(const Web3Transaction& tx) noexcept; -Header headerTxBase(const Web3Transaction& tx) noexcept; -Header header(const Web3Transaction& tx) noexcept; -void encode(bcos::bytes& out, const Web3Transaction&) noexcept; -bcos::Error::UniquePtr decode(bcos::bytesRef& in, AccessListEntry&) noexcept; -bcos::Error::UniquePtr decode(bcos::bytesRef& in, Web3Transaction&) noexcept; +size_t length(const rpc::Web3Transaction&) noexcept; +Header headerForSign(const rpc::Web3Transaction& tx) noexcept; +Header headerTxBase(const rpc::Web3Transaction& tx) noexcept; +Header header(const rpc::Web3Transaction& tx) noexcept; +void encode(bcos::bytes& out, const rpc::Web3Transaction&) noexcept; +bcos::Error::UniquePtr decode(bcos::bytesRef& in, rpc::AccessListEntry&) noexcept; +bcos::Error::UniquePtr decode(bcos::bytesRef& in, rpc::Web3Transaction&) noexcept; } // namespace codec::rlp } // namespace bcos diff --git a/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp b/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp index 686b9a8232..5585cdad26 100644 --- a/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp +++ b/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp @@ -50,7 +50,7 @@ BOOST_AUTO_TEST_CASE(testLegacyTransactionDecode) Web3Transaction tx{}; auto e = codec::rlp::decode(bRef, tx); BOOST_CHECK(!e); - BOOST_CHECK(tx.type == TransactionType::Legacy); + BOOST_CHECK(tx.type == rpc::TransactionType::Legacy); BOOST_CHECK(tx.chainId.has_value()); BOOST_CHECK_EQUAL(tx.chainId.value(), 1); BOOST_CHECK_EQUAL(tx.nonce, 12); @@ -85,7 +85,7 @@ BOOST_AUTO_TEST_CASE(testEIP2930Transaction) Web3Transaction tx{}; auto e = codec::rlp::decode(bRef, tx); BOOST_CHECK(e == nullptr); - BOOST_CHECK(tx.type == TransactionType::EIP2930); + BOOST_CHECK(tx.type == rpc::TransactionType::EIP2930); BOOST_CHECK(tx.chainId.has_value()); BOOST_CHECK_EQUAL(tx.chainId.value(), 5); BOOST_CHECK_EQUAL(tx.nonce, 7); @@ -118,7 +118,7 @@ BOOST_AUTO_TEST_CASE(testEIP1559Transaction) Web3Transaction tx{}; auto e = codec::rlp::decode(bRef, tx); BOOST_CHECK(e == nullptr); - BOOST_CHECK(tx.type == TransactionType::EIP1559); + BOOST_CHECK(tx.type == rpc::TransactionType::EIP1559); BOOST_CHECK(tx.chainId.has_value()); BOOST_CHECK_EQUAL(tx.chainId.value(), 5); BOOST_CHECK_EQUAL(tx.nonce, 7); @@ -151,7 +151,7 @@ BOOST_AUTO_TEST_CASE(testEIP4844Transaction) Web3Transaction tx{}; auto e = codec::rlp::decode(bRef, tx); BOOST_CHECK(e == nullptr); - BOOST_CHECK(tx.type == TransactionType::EIP4844); + BOOST_CHECK(tx.type == rpc::TransactionType::EIP4844); BOOST_CHECK(tx.chainId.has_value()); BOOST_CHECK_EQUAL(tx.chainId.value(), 5); BOOST_CHECK_EQUAL(tx.nonce, 7); @@ -190,7 +190,7 @@ BOOST_AUTO_TEST_CASE(recoverAddress) Web3Transaction tx{}; auto e = codec::rlp::decode(bRef, tx); BOOST_CHECK(!e); - BOOST_CHECK(tx.type == TransactionType::Legacy); + BOOST_CHECK(tx.type == rpc::TransactionType::Legacy); bcos::bytes encoded{}; codec::rlp::encode(encoded, tx); auto rawTx2 = toHexStringWithPrefix(encoded); @@ -221,7 +221,7 @@ BOOST_AUTO_TEST_CASE(EIP1559Recover) Web3Transaction tx{}; auto e = codec::rlp::decode(bRef, tx); BOOST_CHECK(e == nullptr); - BOOST_CHECK(tx.type == TransactionType::EIP1559); + BOOST_CHECK(tx.type == rpc::TransactionType::EIP1559); BOOST_CHECK(tx.chainId.has_value()); BOOST_CHECK_EQUAL(tx.chainId.value(), 1); BOOST_CHECK_EQUAL(tx.nonce, 88852); @@ -264,7 +264,7 @@ BOOST_AUTO_TEST_CASE(EIP4844Recover) Web3Transaction tx{}; auto e = codec::rlp::decode(bRef, tx); BOOST_CHECK(e == nullptr); - BOOST_CHECK(tx.type == TransactionType::EIP4844); + BOOST_CHECK(tx.type == rpc::TransactionType::EIP4844); BOOST_CHECK(tx.chainId.has_value()); BOOST_CHECK_EQUAL(tx.chainId.value(), 1); BOOST_CHECK_EQUAL(tx.nonce, 536312); diff --git a/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.h b/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.h index 1a593695b1..100d51b59a 100644 --- a/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.h +++ b/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.h @@ -65,6 +65,7 @@ class TransactionReceiptImpl : public bcos::protocol::TransactionReceipt int32_t status() const override; bcos::bytesConstRef output() const override; gsl::span logEntries() const override; + bcos::protocol::LogEntries&& takeLogEntris() override { return std::move(m_logEntries); } bcos::protocol::BlockNumber blockNumber() const override; std::string_view effectiveGasPrice() const override; void setEffectiveGasPrice(std::string effectiveGasPrice) override; From 12bbb250b249b04211c350c69ebdab60abda83a1 Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Wed, 17 Apr 2024 10:51:14 +0800 Subject: [PATCH 12/39] (rpc): add block response, fix metamask joint debug issues. (#4370) --- bcos-crypto/bcos-crypto/ChecksumAddress.h | 5 +- bcos-rpc/bcos-rpc/jsonrpc/Common.h | 1 + bcos-rpc/bcos-rpc/validator/JsonValidator.cpp | 15 +- .../bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp | 53 +++-- .../endpoints/EndpointsMapping.cpp | 4 +- .../web3jsonrpc/endpoints/EthEndpoint.cpp | 151 +++++++++--- .../web3jsonrpc/model/BlockResponse.h | 31 ++- .../web3jsonrpc/model/ReceiptResponse.h | 104 ++++++++ .../web3jsonrpc/model/TransactionResponse.h | 91 +++++++ .../web3jsonrpc/model/Web3Transaction.cpp | 37 ++- .../web3jsonrpc/model/Web3Transaction.h | 27 ++- bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.h | 7 + bcos-rpc/test/CMakeLists.txt | 2 +- bcos-rpc/test/unittests/common/RPCFixture.h | 22 +- bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp | 225 +++++++++++++++++- bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp | 4 + .../bcos-utilities/DataConvertUtility.h | 11 +- 17 files changed, 702 insertions(+), 88 deletions(-) create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h diff --git a/bcos-crypto/bcos-crypto/ChecksumAddress.h b/bcos-crypto/bcos-crypto/ChecksumAddress.h index dcf3cff11f..2adb2fd3ff 100644 --- a/bcos-crypto/bcos-crypto/ChecksumAddress.h +++ b/bcos-crypto/bcos-crypto/ChecksumAddress.h @@ -27,7 +27,8 @@ namespace bcos { -inline void toChecksumAddress(std::string& _addr, const std::string_view& addressHashHex) +inline void toChecksumAddress( + std::string& _addr, const std::string_view& addressHashHex, std::string_view prefix = "") { auto convertHexCharToInt = [](char byte) { int ret = 0; @@ -45,7 +46,7 @@ inline void toChecksumAddress(std::string& _addr, const std::string_view& addres } return ret; }; - for (size_t i = 0; i < _addr.size(); ++i) + for (size_t i = prefix.size(); i < _addr.size(); ++i) { if (isdigit(_addr[i])) { diff --git a/bcos-rpc/bcos-rpc/jsonrpc/Common.h b/bcos-rpc/bcos-rpc/jsonrpc/Common.h index bd2e0ca8aa..6f44a3e3df 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/Common.h +++ b/bcos-rpc/bcos-rpc/jsonrpc/Common.h @@ -25,6 +25,7 @@ #include #define RPC_IMPL_LOG(LEVEL) BCOS_LOG(LEVEL) << "[RPC][JSONRPC]" +#define WEB3_LOG(LEVEL) BCOS_LOG(LEVEL) << "[RPC][WEB3]" namespace bcos { diff --git a/bcos-rpc/bcos-rpc/validator/JsonValidator.cpp b/bcos-rpc/bcos-rpc/validator/JsonValidator.cpp index da81769a2b..a90685dadd 100644 --- a/bcos-rpc/bcos-rpc/validator/JsonValidator.cpp +++ b/bcos-rpc/bcos-rpc/validator/JsonValidator.cpp @@ -64,9 +64,20 @@ std::tuple JsonValidator::checkRequestFields(const Json::Valu } else if (item.name() == "id") { - if (!item->isInt()) + if (!item->isUInt64()) { - return {false, "Invalid field: " + item.name()}; + if (item->isString()) + { + if (std::string idString = item->asString(); + !std::regex_match(idString, std::regex("^[0-9a-fA-F-]+$"))) + { + return {false, "Invalid field: " + item.name()}; + } + } + else + { + return {false, "Invalid field: " + item.name()}; + } } flag &= 0x0111; } diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp index 0444aa73d6..cadde5722a 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp @@ -34,29 +34,49 @@ void Web3JsonRpcImpl::onRPCRequest(std::string_view _requestBody, Sender _sender { BOOST_THROW_EXCEPTION(JsonRpcException(InvalidRequest, msg)); } + response["id"] = request["id"]; if (auto const handler = m_endpointsMapping.findHandler(request["method"].asString()); handler.has_value()) { if (c_fileLogLevel == TRACE) [[unlikely]] { - RPC_IMPL_LOG(TRACE) << LOG_BADGE("onRPCRequest") << LOG_KV("request", _requestBody); + WEB3_LOG(TRACE) << LOG_BADGE("Web3Request") + << LOG_KV("request", printJson(request)); } task::wait([](Web3JsonRpcImpl* self, EndpointsMapping::Handler _handler, Json::Value _request, Sender sender) -> task::Task { - Json::Value const& params = _request["params"]; Json::Value resp; - co_await (self->m_endpoints.*_handler)(params, resp); - resp["id"] = _request["id"]; + try + { + // FIXME: throw exception here will core dump + Json::Value const& params = _request["params"]; + + co_await (self->m_endpoints.*_handler)(params, resp); + resp["id"] = _request["id"]; + } + catch (const JsonRpcException& e) + { + buildJsonError(_request, e.code(), e.msg(), resp); + } + catch (bcos::Error const& e) + { + buildJsonError(_request, InternalError, e.errorMessage(), resp); + } + catch (const std::exception& e) + { + buildJsonError(_request, InternalError, boost::diagnostic_information(e), resp); + } auto&& respBytes = toBytesResponse(resp); if (c_fileLogLevel == TRACE) [[unlikely]] { - RPC_IMPL_LOG(TRACE) - << LOG_BADGE("onRPCRequest") + std::string method = _request["method"].asString(); + WEB3_LOG(TRACE) + << LOG_BADGE("Web3Response") << LOG_KV("method", method) << LOG_KV("response", std::string_view((const char*)(respBytes.data()), respBytes.size())); } sender(std::move(respBytes)); - }(this, handler.value(), std::move(request), std::move(_sender))); + }(this, handler.value(), std::move(request), _sender)); return; } BOOST_THROW_EXCEPTION(JsonRpcException(MethodNotFound, "Method not found")); @@ -65,27 +85,14 @@ void Web3JsonRpcImpl::onRPCRequest(std::string_view _requestBody, Sender _sender { buildJsonError(request, e.code(), e.msg(), response); } - catch (bcos::Error const& e) - { - buildJsonError(request, InternalError, e.errorMessage(), response); - } - catch (const boost::exception& e) - { - buildJsonError(request, InternalError, boost::diagnostic_information(e), response); - } - catch (const std::exception& e) - { - buildJsonError(request, InternalError, e.what(), response); - } catch (...) { buildJsonError(request, InternalError, "Internal error", response); } auto&& resp = toBytesResponse(response); - RPC_IMPL_LOG(DEBUG) << LOG_BADGE("onRPCRequest") << LOG_DESC("response with exception") - << LOG_KV("request", _requestBody) - << LOG_KV( - "response", std::string_view((const char*)resp.data(), resp.size())); + WEB3_LOG(DEBUG) << LOG_BADGE("onRPCRequest") << LOG_DESC("response with exception") + << LOG_KV("request", _requestBody) + << LOG_KV("response", std::string_view((const char*)resp.data(), resp.size())); _sender(std::move(resp)); } diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointsMapping.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointsMapping.cpp index df4797392f..6094f43baf 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointsMapping.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EndpointsMapping.cpp @@ -44,9 +44,9 @@ void EndpointsMapping::addHandlers() addWeb3Handlers(); for (auto& [method, _] : m_handlers) { - RPC_IMPL_LOG(INFO) << LOG_BADGE("initHandler") << LOG_KV("method", method); + WEB3_LOG(INFO) << LOG_BADGE("initHandler") << LOG_KV("method", method); } - RPC_IMPL_LOG(INFO) << LOG_BADGE("initHandler") << LOG_KV("size", m_handlers.size()); + WEB3_LOG(INFO) << LOG_BADGE("initHandler") << LOG_KV("size", m_handlers.size()); } void EndpointsMapping::addEthHandlers() diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp index 3ee294ed10..fe6d32b5f6 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp @@ -27,8 +27,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -38,12 +40,14 @@ using namespace bcos::rpc; task::Task EthEndpoint::protocolVersion(const Json::Value&, Json::Value&) { + // TODO: impl this BOOST_THROW_EXCEPTION( JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); co_return; } task::Task EthEndpoint::syncing(const Json::Value&, Json::Value& response) { + // TODO: impl this Json::Value result = false; buildJsonContent(result, response); co_return; @@ -56,7 +60,6 @@ task::Task EthEndpoint::coinbase(const Json::Value&, Json::Value&) } task::Task EthEndpoint::chainId(const Json::Value&, Json::Value& response) { - // TODO: get chain id Json::Value result = "0x4ee8"; // 20200 buildJsonContent(result, response); co_return; @@ -103,6 +106,8 @@ task::Task EthEndpoint::getBalance(const Json::Value& request, Json::Value // params: address(DATA), blockNumber(QTY|TAG) // result: balance(QTY) // TODO: get balance from ledger + Json::Value result = "0xfffffffffffffff"; + buildJsonContent(result, response); co_return; } task::Task EthEndpoint::getStorageAt(const Json::Value&, Json::Value&) @@ -118,6 +123,18 @@ task::Task EthEndpoint::getTransactionCount(const Json::Value& request, Js // params: address(DATA), blockNumber(QTY|TAG) // result: nonce(QTY) // TODO: impliment getTransactionCount + static thread_local std::mt19937 generator(std::random_device{}()); + std::uniform_int_distribution dis(0, 255); + std::array randomFixedBytes; + for (auto& element : randomFixedBytes) + { + element = dis(generator); + } + + uint16_t* firstNum = (uint16_t*)randomFixedBytes.data(); + // uint64_t* lastNum = (uint64_t*)(randomFixedBytes.data() + sizeof(uint64_t)); + Json::Value result = toQuantity(*firstNum); + buildJsonContent(result, response); co_return; } task::Task EthEndpoint::getBlockTxCountByHash( @@ -214,17 +231,26 @@ task::Task EthEndpoint::sendRawTransaction(const Json::Value& request, Jso auto tx = std::make_shared( [m_tx = std::move(tarsTx)]() mutable { return &m_tx; }); + // for web3.eth.sendRawTransaction, return the hash of raw transaction + auto web3TxHash = bcos::crypto::keccak256Hash(bcos::ref(rawTxBytes)); + tx->mutableInner().dataHash.assign(web3TxHash.begin(), web3TxHash.end()); if (c_fileLogLevel == TRACE) { - RPC_IMPL_LOG(TRACE) << LOG_DESC("sendRawTransaction") << web3Tx.toString(); + WEB3_LOG(TRACE) << LOG_DESC("sendRawTransaction") << web3Tx.toString(); } txpool->broadcastTransaction(*tx); auto const txResult = co_await txpool->submitTransactionWithoutReceipt(std::move(tx)); crypto::HashType hash{}; if (txResult->status() == 0) { - hash = tx->hash(); + hash = std::move(web3TxHash); + } + if (c_fileLogLevel == TRACE) + { + WEB3_LOG(TRACE) << LOG_DESC("sendRawTransaction finished") + << LOG_KV("status", txResult->status()) + << LOG_KV("hash", hash.hexPrefixed()); } Json::Value result = hash.hexPrefixed(); buildJsonContent(result, response); @@ -247,7 +273,7 @@ task::Task EthEndpoint::call(const Json::Value& request, Json::Value& resp } if (c_fileLogLevel == TRACE) { - RPC_IMPL_LOG(TRACE) << LOG_DESC("eth_call") << call; + WEB3_LOG(TRACE) << LOG_DESC("eth_call") << call; } auto&& tx = call.takeToTransaction(m_nodeService->blockFactory()->transactionFactory()); // TODO: ignore params blockNumber here, use it after historical data is available @@ -284,16 +310,16 @@ task::Task EthEndpoint::call(const Json::Value& request, Json::Value& resp }; auto const result = co_await Awaitable{.m_scheduler = *scheduler, .m_tx = std::move(tx)}; - Json::Value jsonResult; auto output = toHexStringWithPrefix(result->output()); if (result->status() == static_cast(protocol::TransactionStatus::None)) { - jsonResult = std::move(output); + response["jsonrpc"] = "2.0"; + response["result"] = std::move(output); } else { // https://docs.infura.io/api/networks/ethereum/json-rpc-methods/eth_call#returns - jsonResult = Json::objectValue; + Json::Value jsonResult = Json::objectValue; jsonResult["code"] = result->status(); jsonResult["message"] = result->message(); jsonResult["data"] = std::move(output); @@ -310,44 +336,105 @@ task::Task EthEndpoint::estimateGas(const Json::Value& request, Json::Valu buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::getBlockByHash(const Json::Value&, Json::Value&) +task::Task EthEndpoint::getBlockByHash(const Json::Value& request, Json::Value& response) { // params: blockHash(DATA), fullTransaction(Boolean) // result: block(BLOCK) - BOOST_THROW_EXCEPTION( - JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); + auto blockHash = toView(request[0u]); + auto fullTransaction = request[1u].asBool(); + auto const ledger = m_nodeService->ledger(); + auto number = co_await ledger::getBlockNumber( + *ledger, crypto::HashType(blockHash, crypto::HashType::FromHex)); + auto block = co_await ledger::getBlockData(*ledger, number, bcos::ledger::FULL_BLOCK); + Json::Value result = Json::objectValue; + combineBlockResponse(result, std::move(block), fullTransaction); + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::getBlockByNumber(const Json::Value&, Json::Value&) +task::Task EthEndpoint::getBlockByNumber(const Json::Value& request, Json::Value& response) { // params: blockNumber(QTY|TAG), fullTransaction(Boolean) // result: block(BLOCK) - BOOST_THROW_EXCEPTION( - JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); + auto blockTag = toView(request[0u]); + auto fullTransaction = request[1u].asBool(); + auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); + auto const ledger = m_nodeService->ledger(); + auto block = co_await ledger::getBlockData(*ledger, blockNumber, bcos::ledger::FULL_BLOCK); + Json::Value result = Json::objectValue; + combineBlockResponse(result, std::move(block), fullTransaction); + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::getTransactionByHash(const Json::Value&, Json::Value&) +task::Task EthEndpoint::getTransactionByHash( + const Json::Value& request, Json::Value& response) { // params: transactionHash(DATA) // result: transaction(TX) - BOOST_THROW_EXCEPTION( - JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); + auto txHash = toView(request[0u]); + auto hash = crypto::HashType(txHash, crypto::HashType::FromHex); + auto hashList = std::make_shared(); + hashList->push_back(hash); + auto const ledger = m_nodeService->ledger(); + auto txs = co_await ledger::getTransactions(*ledger, std::move(hashList)); + auto receipt = co_await ledger::getReceipt(*ledger, hash); + if (!receipt || !txs || txs->empty()) + { + BOOST_THROW_EXCEPTION( + JsonRpcException(InvalidParams, "Invalid transaction hash: " + hash.hexPrefixed())); + } + auto block = co_await ledger::getBlockData( + *ledger, receipt->blockNumber(), bcos::ledger::HEADER | bcos::ledger::TRANSACTIONS_HASH); + Json::Value result = Json::objectValue; + combineTxResponse(result, txs->at(0), std::move(receipt), std::move(block)); + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::getTransactionByBlockHashAndIndex(const Json::Value&, Json::Value&) +task::Task EthEndpoint::getTransactionByBlockHashAndIndex( + const Json::Value& request, Json::Value& response) { // params: blockHash(DATA), transactionIndex(QTY) // result: transaction(TX) - BOOST_THROW_EXCEPTION( - JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); - co_return; -} -task::Task EthEndpoint::getTransactionByBlockNumberAndIndex(const Json::Value&, Json::Value&) + // auto blockHash = toView(request[0u]); + // auto transactionIndex = fromQuantity(std::string(toView(request[1u]))); + // auto hash = crypto::HashType(blockHash, crypto::HashType::FromHex); + // auto const ledger = m_nodeService->ledger(); + // auto number = co_await ledger::getBlockNumber(*ledger, hash); + // if (number <= 0) [[unlikely]] + // { + // BOOST_THROW_EXCEPTION( + // JsonRpcException(InvalidParams, "Invalid block hash: " + hash.hexPrefixed())); + // } + // auto block = co_await ledger::getBlockData( + // *ledger, number, bcos::ledger::TRANSACTIONS | bcos::ledger::HEADER); + // if (!block || transactionIndex >= block->transactionsSize()) [[unlikely]] + // { + // BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParams, "Invalid transaction index!")); + // } + // auto tx = block->transaction(transactionIndex); + // Json::Value result = Json::objectValue; + // combineTxResponse(result, std::move(tx), nullptr, std::move(block)); + // buildJsonContent(result, response); + co_return; +} +task::Task EthEndpoint::getTransactionByBlockNumberAndIndex( + const Json::Value& request, Json::Value& response) { // params: blockNumber(QTY|TAG), transactionIndex(QTY) // result: transaction(TX) - BOOST_THROW_EXCEPTION( - JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); + // auto blockTag = toView(request[0u]); + // auto transactionIndex = fromQuantity(std::string(toView(request[1u]))); + // auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); + // auto const ledger = m_nodeService->ledger(); + // auto block = co_await ledger::getBlockData( + // *ledger, blockNumber, bcos::ledger::TRANSACTIONS | bcos::ledger::HEADER); + // if (!block || transactionIndex >= block->transactionsSize()) [[unlikely]] + // { + // BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParams, "Invalid transaction index!")); + // } + // auto tx = block->transaction(transactionIndex); + // Json::Value result = Json::objectValue; + // combineTxResponse(result, std::move(tx), nullptr, std::move(block)); + // buildJsonContent(result, response); co_return; } task::Task EthEndpoint::getTransactionReceipt( @@ -359,22 +446,18 @@ task::Task EthEndpoint::getTransactionReceipt( auto hash = crypto::HashType(hashStr, crypto::HashType::FromHex); auto const ledger = m_nodeService->ledger(); auto receipt = co_await ledger::getReceipt(*ledger, hash); - auto receiptResponse = ReceiptResponse(std::move(receipt)); auto hashList = std::make_shared(); hashList->push_back(hash); auto txs = co_await ledger::getTransactions(*ledger, std::move(hashList)); - if (txs->size() == 1) + if (!receipt || !txs || txs->empty()) { - receiptResponse.updateTxInfo(*txs->at(0)); - } - auto block = co_await ledger::getBlockData(*ledger, receiptResponse.blockNumber, - bcos::ledger::HEADER & bcos::ledger::TRANSACTIONS_HASH); - if (block) [[likely]] - { - receiptResponse.updateBlockInfo(*block); + BOOST_THROW_EXCEPTION( + JsonRpcException(InvalidParams, "Invalid transaction hash: " + hash.hexPrefixed())); } + auto block = co_await ledger::getBlockData( + *ledger, receipt->blockNumber(), bcos::ledger::HEADER | bcos::ledger::TRANSACTIONS_HASH); Json::Value result = Json::objectValue; - receiptResponse.toJson(result); + combineReceiptResponse(result, std::move(receipt), txs->at(0), std::move(block)); buildJsonContent(result, response); co_return; } diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.h index a7de6c20bc..939add14d7 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.h @@ -19,6 +19,7 @@ */ #pragma once +#include #include #include #include @@ -26,7 +27,33 @@ namespace bcos::rpc { -class BlockResponse +[[maybe_unused]] static void combineBlockResponse( + Json::Value& result, bcos::protocol::Block::Ptr&& block, bool fullTxs = false) { -}; + result["number"] = toQuantity(block->blockHeader()->number()); + result["hash"] = block->blockHeader()->hash().hexPrefixed(); + for (const auto& info : block->blockHeader()->parentInfo()) + { + result["parentHash"] = info.blockHash.hexPrefixed(); + } + result["nonce"] = "0x0000000000000000"; + // result["sha3Uncles"] = "0x"; + // result["logsBloom"] = "0x"; + result["transactionsRoot"] = block->blockHeader()->txsRoot().hexPrefixed(); + result["stateRoot"] = block->blockHeader()->stateRoot().hexPrefixed(); + result["receiptsRoot"] = block->blockHeader()->receiptsRoot().hexPrefixed(); + result["miner"] = Address().hexPrefixed(); + result["difficulty"] = "0x0"; + result["totalDifficulty"] = "0x0"; + result["extraData"] = toHexStringWithPrefix(block->blockHeader()->extraData()); + result["size"] = "0xffff"; + result["gasLimit"] = toQuantity(3000000000ull); + result["gasUsed"] = toQuantity((uint64_t)block->blockHeader()->gasUsed()); + result["timestamp"] = toQuantity(block->blockHeader()->timestamp()); + // if (fullTxs) + // { + // result["transactions"] = Json::Value(Json::arrayValue); + // } + result["uncles"] = Json::Value(Json::arrayValue); +} } // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h index 2e5e6bf321..8f123dabb8 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h @@ -29,10 +29,12 @@ #include #include +#include #include namespace bcos::rpc { +// maybe this struct is unnecessary... struct ReceiptResponse { ReceiptResponse() = default; @@ -103,4 +105,106 @@ struct ReceiptResponse void toJson(Json::Value&) {} }; +[[maybe_unused]] static void combineReceiptResponse(Json::Value& result, + protocol::TransactionReceipt::ConstPtr&& receipt, bcos::protocol::Transaction::ConstPtr&& tx, + bcos::protocol::Block::Ptr&& block) +{ + if (!result.isObject()) + { + return; + } + uint8_t status = (receipt->status() == 0 ? 1 : 0); + result["status"] = toQuantity(status); + result["transactionHash"] = tx->hash().hexPrefixed(); + size_t transactionIndex = 0; + crypto::HashType blockHash; + uint64_t blockNumber = 0; + if (block) + { + blockHash = block->blockHeader()->hash(); + blockNumber = block->blockHeader()->number(); + for (; transactionIndex < block->transactionsHashSize(); transactionIndex++) + { + if (block->transactionHash(transactionIndex) == tx->hash()) + { + break; + } + } + } + result["transactionIndex"] = toQuantity(transactionIndex); + result["blockHash"] = blockHash.hexPrefixed(); + result["blockNumber"] = toQuantity(blockNumber); + auto from = toHexStringWithPrefix(tx->sender()); + toChecksumAddress(from, bcos::crypto::keccak256Hash(from).hexPrefixed(), "0x"); + result["from"] = std::move(from); + if (tx->to().empty()) + { + result["to"] = ""; + } + else + { + auto to = std::string(tx->to()); + toChecksumAddress(to, bcos::crypto::keccak256Hash(to).hex()); + result["to"] = "0x" + std::move(to); + } + result["cumulativeGasUsed"] = "0x0"; + result["effectiveGasPrice"] = + receipt->effectiveGasPrice().empty() ? "0x0" : std::string(receipt->effectiveGasPrice()); + result["gasUsed"] = toQuantity((uint64_t)receipt->gasUsed()); + if (receipt->contractAddress().empty()) + { + result["contractAddress"] = Json::nullValue; + } + else + { + auto contractAddress = std::string(receipt->contractAddress()); + toChecksumAddress(contractAddress, bcos::crypto::keccak256Hash(contractAddress).hex()); + result["contractAddress"] = "0x" + std::move(contractAddress); + } + result["logs"] = Json::arrayValue; + auto mutableReceipt = const_cast(receipt.get()); + auto receiptLog = mutableReceipt->takeLogEntris(); + for (size_t i = 0; i < receiptLog.size(); i++) + { + Json::Value log; + auto address = std::string(receiptLog[i].address()); + toChecksumAddress(address, bcos::crypto::keccak256Hash(address).hex()); + log["address"] = "0x" + std::move(address); + log["topics"] = Json::arrayValue; + for (const auto& topic : receiptLog[i].topics()) + { + log["topics"].append(topic.hexPrefixed()); + } + log["data"] = toHexStringWithPrefix(receiptLog[i].data()); + log["logIndex"] = toQuantity(i); + log["blockNumber"] = toQuantity(blockNumber); + log["blockHash"] = blockHash.hexPrefixed(); + log["transactionIndex"] = toQuantity(transactionIndex); + log["transactionHash"] = receipt->hash().hexPrefixed(); + log["removed"] = false; + result["logs"].append(std::move(log)); + } + Logs logs; + logs.reserve(receiptLog.size()); + for (size_t i = 0; i < receiptLog.size(); i++) + { + rpc::Log log{.address = std::move(receiptLog[i].takeAddress()), + .topics = std::move(receiptLog[i].takeTopics()), + .data = std::move(receiptLog[i].takeData())}; + log.logIndex = i; + logs.push_back(std::move(log)); + } + auto logsBloom = getLogsBloom(logs); + result["logsBloom"] = toHexStringWithPrefix(logsBloom); + auto type = TransactionType::Legacy; + if (!tx->extraTransactionBytes().empty()) + { + if (auto firstByte = tx->extraTransactionBytes()[0]; + firstByte < bcos::codec::rlp::BYTES_HEAD_BASE) + { + type = static_cast(firstByte); + } + } + result["type"] = toQuantity(static_cast(type)); +} } // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h new file mode 100644 index 0000000000..fb411ad18e --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h @@ -0,0 +1,91 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file TransactionResponse.h + * @author: kyonGuo + * @date 2024/4/16 + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace bcos::rpc +{ +// block and receipt are nullable +static void combineTxResponse(Json::Value& result, bcos::protocol::Transaction::ConstPtr&& tx, + protocol::TransactionReceipt::ConstPtr&& receipt, bcos::protocol::Block::Ptr&& block) +{ + if (!result.isObject()) + { + return; + } + size_t transactionIndex = 0; + crypto::HashType blockHash; + uint64_t blockNumber = 0; + if (block) + { + blockHash = block->blockHeader()->hash(); + blockNumber = block->blockHeader()->number(); + for (; transactionIndex < block->transactionsHashSize(); transactionIndex++) + { + if (block->transactionHash(transactionIndex) == tx->hash()) + { + break; + } + } + } + result["blockHash"] = blockHash.hexPrefixed(); + result["blockNumber"] = toQuantity(blockNumber); + result["transactionIndex"] = toQuantity(transactionIndex); + auto from = toHexStringWithPrefix(tx->sender()); + toChecksumAddress(from, bcos::crypto::keccak256Hash(from).hexPrefixed(), "0x"); + result["from"] = std::move(from); + if (tx->to().empty()) + { + result["to"] = Json::nullValue; + } + else + { + auto to = std::string(tx->to()); + toChecksumAddress(to, bcos::crypto::keccak256Hash(to).hex()); + result["to"] = "0x" + std::move(to); + } + result["gas"] = toQuantity(tx->gasLimit()); + result["gasPrice"] = std::string(receipt ? receipt->effectiveGasPrice() : tx->gasPrice()); + result["hash"] = tx->hash().hexPrefixed(); + result["input"] = toHexStringWithPrefix(tx->input()); + Web3Transaction web3Tx; + auto extraBytesRef = bcos::bytesRef( + const_cast(tx->extraTransactionBytes().data()), tx->extraTransactionBytes().size()); + codec::rlp::decode(extraBytesRef, web3Tx); + result["nonce"] = toQuantity(web3Tx.nonce); + result["type"] = toQuantity(static_cast(web3Tx.type)); + result["value"] = toQuantity(web3Tx.value); + result["r"] = toQuantity(tx->signatureData().getCroppedData(0, 32)); + result["s"] = toQuantity(tx->signatureData().getCroppedData(32, 32)); + result["v"] = toQuantity(tx->signatureData().getCroppedData(64, 1)); + if (web3Tx.type >= TransactionType::EIP1559) + { + result["maxPriorityFeePerGas"] = toQuantity(web3Tx.maxPriorityFeePerGas); + result["maxFeePerGas"] = toQuantity(web3Tx.maxFeePerGas); + } + result["chainId"] = toQuantity(web3Tx.chainId.value_or(0)); +} +} // namespace bcos::rpc diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp index cd31d242f6..33ebaae179 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp @@ -21,6 +21,9 @@ #include "Web3Transaction.h" #include +#include +#include +#include namespace bcos { @@ -41,7 +44,14 @@ bcos::bytes Web3Transaction::encode() const // for legacy tx, it means gas price codec::rlp::encode(out, maxFeePerGas); codec::rlp::encode(out, gasLimit); - codec::rlp::encode(out, to.ref()); + if (to != Address()) + { + codec::rlp::encode(out, to.ref()); + } + else + { + out.push_back(codec::rlp::BYTES_HEAD_BASE); + } codec::rlp::encode(out, value); codec::rlp::encode(out, data); if (chainId) @@ -95,19 +105,19 @@ bcos::crypto::HashType Web3Transaction::hash() const bcostars::Transaction Web3Transaction::toTarsTransaction() const { bcostars::Transaction tarsTx{}; - tarsTx.data.nonce = std::to_string(this->nonce); - tarsTx.data.to = this->to.hexPrefixed(); + tarsTx.data.nonce = toHex(this->nonce); + tarsTx.data.to = (this->to == Address()) ? "" : this->to.hexPrefixed(); tarsTx.data.input.insert(tarsTx.data.input.end(), this->data.begin(), this->data.end()); - tarsTx.data.value = std::to_string(this->value); + tarsTx.data.value = toHex(this->value); tarsTx.data.gasLimit = this->gasLimit; if (static_cast(this->type) >= static_cast(TransactionType::EIP1559)) { - tarsTx.data.maxFeePerGas = std::to_string(this->maxFeePerGas); - tarsTx.data.maxPriorityFeePerGas = std::to_string(this->maxPriorityFeePerGas); + tarsTx.data.maxFeePerGas = toHex(this->maxFeePerGas); + tarsTx.data.maxPriorityFeePerGas = toHex(this->maxPriorityFeePerGas); } else { - tarsTx.data.gasPrice = std::to_string(this->maxPriorityFeePerGas); + tarsTx.data.gasPrice = toHex(this->maxPriorityFeePerGas); } auto hash = this->hash(); auto encodedForSign = this->encode(); @@ -119,7 +129,7 @@ bcostars::Transaction Web3Transaction::toTarsTransaction() const tarsTx.signature.end(), this->signatureS.begin(), this->signatureS.end()); tarsTx.signature.push_back(static_cast(this->signatureV)); - tarsTx.type = 1; + tarsTx.type = static_cast(bcos::protocol::TransactionType::Web3Transacion); tarsTx.extraTransactionBytes.insert( tarsTx.extraTransactionBytes.end(), encodedForSign.begin(), encodedForSign.end()); @@ -158,7 +168,7 @@ Header headerTxBase(const Web3Transaction& tx) noexcept } h.payloadLength += length(tx.maxFeePerGas); h.payloadLength += length(tx.gasLimit); - h.payloadLength += tx.to ? (Address::SIZE + 1) : 1; + h.payloadLength += (tx.to != Address()) ? (Address::SIZE + 1) : 1; h.payloadLength += length(tx.value); h.payloadLength += length(tx.data); @@ -339,6 +349,15 @@ bcos::Error::UniquePtr decode(bcos::bytesRef& in, Web3Transaction& out) noexcept out.type = TransactionType::Legacy; auto decodeError = decodeItems(in, out.nonce, out.maxPriorityFeePerGas, out.gasLimit, out.to, out.value, out.data, out.signatureV, out.signatureR, out.signatureS); + if (out.signatureR.size() < crypto::SECP256K1_SIGNATURE_LEN / 2) + { + out.signatureR.insert(out.signatureR.begin(), bcos::byte(0)); + } + if (out.signatureS.size() < crypto::SECP256K1_SIGNATURE_LEN / 2) + { + out.signatureS.insert(out.signatureS.begin(), bcos::byte(0)); + } + // TODO: EIP-155 chainId decode from encoded bytes for sign out.maxFeePerGas = out.maxPriorityFeePerGas; auto v = out.signatureV; if (v == 27 || v == 28) diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h index 5ddedbd767..2840efe113 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,13 @@ enum class TransactionType : uint8_t EIP4844 = 3, // https://eips.ethereum.org/EIPS/eip-4844 }; +[[maybe_unused]] constexpr auto operator<=>(TransactionType const& ltype, auto rtype) + requires std::same_as || + std::unsigned_integral +{ + return static_cast(ltype) <=> static_cast(rtype); +} + [[maybe_unused]] static std::ostream& operator<<(std::ostream& _out, const TransactionType& _in) { _out << magic_enum::enum_name(_in); @@ -81,11 +89,26 @@ class Web3Transaction return signatureV + 27; } + // std::string sender() const + // { + // bcos::bytes sign{}; + // sign.reserve(crypto::SECP256K1_SIGNATURE_LEN); + // sign.insert(sign.end(), signatureR.begin(), signatureR.end()); + // sign.insert(sign.end(), signatureS.begin(), signatureS.end()); + // sign.push_back(signatureV); + // bcos::crypto::Keccak256 hashImpl; + // auto encodeForSign = encode(); + // auto hash = bcos::crypto::keccak256Hash(ref(encodeForSign)); + // const bcos::crypto::Secp256k1Crypto signatureImpl; + // auto [_, addr] = signatureImpl.recoverAddress(hashImpl, hash, ref(sign)); + // return toHexStringWithPrefix(addr); + // } + std::string toString() const noexcept { std::stringstream stringstream{}; - stringstream << "chainId: " << this->chainId.value_or(0) - << " type: " << static_cast(this->type) << " to: " << this->to + stringstream << " chainId: " << this->chainId.value_or(0) + << " type: " << static_cast(this->type) << " to: " << this->to << " data: " << toHex(this->data) << " value: " << this->value << " nonce: " << this->nonce << " gasLimit: " << this->gasLimit << " maxPriorityFeePerGas: " << this->maxPriorityFeePerGas diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.h b/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.h index b59d88af6c..6a0f3dc802 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.h @@ -27,6 +27,13 @@ namespace bcos::rpc void buildJsonContent(Json::Value& result, Json::Value& response); void buildJsonError( Json::Value const& request, int32_t code, std::string message, Json::Value& response); +inline auto printJson(const Json::Value& value) +{ + Json::StreamWriterBuilder builder; + builder["commentStyle"] = "None"; + builder["indentation"] = ""; + return Json::writeString(builder, value); +} inline std::string_view toView(const Json::Value& value) { const char* begin = nullptr; diff --git a/bcos-rpc/test/CMakeLists.txt b/bcos-rpc/test/CMakeLists.txt index 1b3783b7d3..b647c52e33 100644 --- a/bcos-rpc/test/CMakeLists.txt +++ b/bcos-rpc/test/CMakeLists.txt @@ -26,6 +26,6 @@ add_executable(${TEST_BINARY_NAME} ${SOURCES}) target_include_directories(${TEST_BINARY_NAME} PRIVATE .) target_compile_options(${TEST_BINARY_NAME} PRIVATE -Wno-unused-variable) -target_link_libraries(${TEST_BINARY_NAME} ${LEDGER_TARGET} ${RPC_TARGET} ${TOOL_TARGET} Boost::unit_test_framework) +target_link_libraries(${TEST_BINARY_NAME} ${TXPOOL_TARGET} ${LEDGER_TARGET} ${RPC_TARGET} ${TOOL_TARGET} Boost::unit_test_framework) #add_test(NAME ${TEST_BINARY_NAME} WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${TEST_BINARY_NAME}) config_test_cases("" "${SOURCES}" ${TEST_BINARY_NAME} "") \ No newline at end of file diff --git a/bcos-rpc/test/unittests/common/RPCFixture.h b/bcos-rpc/test/unittests/common/RPCFixture.h index d23e1f9ff4..f71cc7c320 100644 --- a/bcos-rpc/test/unittests/common/RPCFixture.h +++ b/bcos-rpc/test/unittests/common/RPCFixture.h @@ -35,9 +35,15 @@ #include #include #include +#include #include #include #include +#include +#include +#include +#include +#include #include #include @@ -82,7 +88,18 @@ class RPCFixture : public TestPromptFixture cryptoSuite, blockHeaderFactory, txFactory, receiptFactory); m_ledger = std::make_shared(m_blockFactory, 20, 10, 10); m_ledger->setSystemConfig(ledger::SYSTEM_KEY_TX_COUNT_LIMIT, "1000"); - txPool = std::make_shared(); + + auto nodeId = std::make_shared( + h256("1110000000000000000000000000000000000000000000000000000000000000").asBytes()); + m_frontService = std::make_shared(nodeId); + auto txResultFactory = std::make_shared(); + + auto txPoolFactory = std::make_shared(nodeId, cryptoSuite, txResultFactory, + m_blockFactory, m_frontService, m_ledger, "group0", "chain0", 100000000); + + txPool = txPoolFactory->createTxPool(); + txPool->init(); + txPool->start(); scheduler = std::make_shared(m_ledger, m_blockFactory); nodeService = std::make_shared( @@ -109,8 +126,9 @@ class RPCFixture : public TestPromptFixture std::string chainId = "test-chain"; RpcFactory::Ptr factory; + FakeFrontService::Ptr m_frontService; FakeLedger::Ptr m_ledger; - FakeTxPool::Ptr txPool; + TxPool::Ptr txPool; FakeScheduler::Ptr scheduler; BlockFactory::Ptr m_blockFactory; diff --git a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp index 746f9277bb..1f6926c634 100644 --- a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp +++ b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp @@ -27,6 +27,8 @@ #include "bcos-crypto/signature/sm2/SM2KeyPair.h" #include "bcos-framework/protocol/GlobalConfig.h" #include "bcos-rpc/bcos-rpc/RpcFactory.h" + +#include #include #include #include @@ -36,6 +38,7 @@ #include #include #include +#include #include #include @@ -71,8 +74,21 @@ class Web3TestFixture : public RPCFixture { BCOS_LOG(ERROR) << (e).what(); } + catch (...) + { + BCOS_LOG(ERROR) << "Unknown exception"; + } return value; } + static void validRespCheck(Json::Value const& resp) + { + BOOST_CHECK(!resp.isMember("error")); + BOOST_CHECK(resp.isMember("result")); + BOOST_CHECK(resp.isMember("id")); + BOOST_CHECK(resp.isMember("jsonrpc")); + BOOST_CHECK(resp["jsonrpc"].asString() == "2.0"); + }; + Rpc::Ptr rpc; Web3JsonRpcImpl::Ptr web3JsonRpc; }; @@ -111,18 +127,21 @@ BOOST_AUTO_TEST_CASE(handleInvalidTest) BOOST_CHECK(response["error"]["code"].asInt() == MethodNotFound); BOOST_CHECK(response["error"]["message"].asString() == "Method not found"); } + + // not impl method + { + const auto request = R"({"jsonrpc":"2.0","id":1, "method":"eth_coinbase","params":[]})"; + auto response = onRPCRequestWrapper(request); + std::string s = response.toStyledString(); + BOOST_CHECK(response.isMember("error")); + BOOST_CHECK(response["error"]["code"].asInt() == MethodNotFound); + BOOST_CHECK( + response["error"]["message"].asString() == "This API has not been implemented yet!"); + } } BOOST_AUTO_TEST_CASE(handleValidTest) { - auto validRespCheck = [](Json::Value const& resp) { - BOOST_CHECK(!resp.isMember("error")); - BOOST_CHECK(resp.isMember("result")); - BOOST_CHECK(resp.isMember("id")); - BOOST_CHECK(resp.isMember("jsonrpc")); - BOOST_CHECK(resp["jsonrpc"].asString() == "2.0"); - }; - // method eth_syncing { const auto request = @@ -193,6 +212,28 @@ BOOST_AUTO_TEST_CASE(handleValidTest) auto const blkNum = toQuantity(m_ledger->blockNumber()); BOOST_CHECK(response["result"].asString() == blkNum); } + + // method eth_blockNumber + { + const auto request = + R"({"id":1655516568316917,"jsonrpc":"2.0","method":"eth_blockNumber","params":[]})"; + auto response = onRPCRequestWrapper(request); + validRespCheck(response); + BOOST_CHECK(response["id"].asUInt64() == 1655516568316917); + auto const blkNum = toQuantity(m_ledger->blockNumber()); + BOOST_CHECK(response["result"].asString() == blkNum); + } + + // method eth_blockNumber + { + const auto request = + R"({"id":"e4df2b99-f80b-4e23-91c7-b9471b46af26","jsonrpc":"2.0","method":"eth_blockNumber","params":[]})"; + auto response = onRPCRequestWrapper(request); + validRespCheck(response); + BOOST_CHECK(response["id"].asString() == "e4df2b99-f80b-4e23-91c7-b9471b46af26"); + auto const blkNum = toQuantity(m_ledger->blockNumber()); + BOOST_CHECK(response["result"].asString() == blkNum); + } } BOOST_AUTO_TEST_CASE(handleWeb3NamespaceValidTest) @@ -227,5 +268,173 @@ BOOST_AUTO_TEST_CASE(handleWeb3NamespaceValidTest) "0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad"); } } + +BOOST_AUTO_TEST_CASE(handleLegacyTxTest) +{ + // method eth_sendRawTransaction + auto testLegacyTx = [&](std::string const& rawTx, std::string const& expectHash) { + // clang-format off + const std::string request = R"({"jsonrpc":"2.0","id":1132123, "method":"eth_sendRawTransaction","params":[")" + rawTx + R"("]})"; + // clang-format on + auto rawTxBytes = fromHexWithPrefix(rawTx); + auto rawTxBytesRef = bcos::ref(rawTxBytes); + Web3Transaction rawWeb3Tx; + codec::rlp::decode(rawTxBytesRef, rawWeb3Tx); + auto response = onRPCRequestWrapper(request); + validRespCheck(response); + BOOST_CHECK(response["id"].asInt64() == 1132123); + BOOST_CHECK(response["result"].asString() == expectHash); + std::vector hashes = {HashType(expectHash)}; + task::wait([&rawWeb3Tx]( + Web3TestFixture* self, decltype(hashes) m_hashes) -> task::Task { + auto txs = co_await self->txPool->getTransactions(m_hashes); + BOOST_CHECK(txs.size() == 1); + BOOST_CHECK(txs[0]->hash() == m_hashes[0]); + BOOST_CHECK(txs[0]->type() == 1); + BOOST_CHECK(!txs[0]->extraTransactionBytes().empty()); + auto ref = bytesRef(const_cast(txs[0]->extraTransactionBytes().data()), + txs[0]->extraTransactionBytes().size()); + Web3Transaction tx; + bcos::codec::rlp::decode(ref, tx); + BOOST_CHECK(tx.type == rawWeb3Tx.type); + BOOST_CHECK(tx.data == rawWeb3Tx.data); + BOOST_CHECK(tx.nonce == rawWeb3Tx.nonce); + BOOST_CHECK(tx.to == rawWeb3Tx.to); + BOOST_CHECK(tx.value == rawWeb3Tx.value); + BOOST_CHECK(tx.maxFeePerGas == rawWeb3Tx.maxFeePerGas); + BOOST_CHECK(tx.maxPriorityFeePerGas == rawWeb3Tx.maxPriorityFeePerGas); + BOOST_CHECK(tx.gasLimit == rawWeb3Tx.gasLimit); + BOOST_CHECK(tx.accessList == rawWeb3Tx.accessList); + // FIXME: chainId recover is wrong + // BOOST_CHECK(tx.chainId == rawWeb3Tx.chainId); + }(this, std::move(hashes))); + return rawWeb3Tx; + }; + + // clang-format off + // https://etherscan.io/tx/0x6f55e12280765243993c76e80e244ecad548aff22380c8e89e060a60deab4b28 + testLegacyTx( + "0xf902ee83031ae9850256506a88831b40d094d56e4eab23cb81f43168f9f45211eb027b9ac7cc80b90284b143044b00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000650000000000000000000000004d73adb72bc3dd368966edd0f0b2148401a178e200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000661d084300000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000084704316e5000000000000000000000000000000000000000000000000000000000000006e740e551c6ee78d757128b8e476b390f891c54e96538e1bb4469a62105220215a0000000000000000000000000000000000000000000000000000000000000014740e551c6ee78d757128b8e476b390f891c54e96538e1bb4469a62105220215a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082ce44cffc92754f8825e1c60cadc5f54a504b769ee616e9f28a9797c2a4b84d11542a1aa4a06a6aa60ee062a36c4fb467145b8914959e42323a13068e2475bff41c67af8dd96034675776cb78036711c1f320b82085df5ee005ffca77959f29a0a07a226241787f06b57483d509e0befd84e5ac84d960e881c7b0853ee1d7c63dff1b00000000000000000000000000000000000000000000000000000000000026a04932608e9743aaa76626f082cedb5da11ff8a1ebe6b5a62a372c81393b5912aea012f36de80608ba3b0f72f3e8f299379ce2802e64b1cbb55275ad9aaa81190b44", + "0x6f55e12280765243993c76e80e244ecad548aff22380c8e89e060a60deab4b28"); + // https://etherscan.io/tx/0x4cbe0f1e383e4aec60cedd0e8dc01832efd160415d82557a72853ec1a58e615b + testLegacyTx("0xf9014d8203ca8504a817c800830f42408080b8f9606060405260008054600160a060020a0319163317905560d78060226000396000f36060604052361560275760e060020a600035046341c0e1b58114606e578063e5225381146096575b60d56000341115606c57346060908152605890600160a060020a033316907f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a1590602090a35b565b60d5600054600160a060020a0390811633919091161415606c57600054600160a060020a0316ff5b60d5600054600160a060020a0390811633919091161415606c5760008054600160a060020a039081169190301631606082818181858883f15050505050565b001ca0c11d1e703f19bd676fcd8cb0c2f6d9febd4c117692215e92a8933f0c34e2ea85a03a9cd0c207899aa699d287025fa13073a461485a59a4fe0aa246db9a2428e7a7", + "0x4cbe0f1e383e4aec60cedd0e8dc01832efd160415d82557a72853ec1a58e615b"); + // https://etherscan.io/tx/0xdd2294fae0bce9409e3f52684b9947613147b9fe2fcb8efb648ca096728236f5 + testLegacyTx("0xf9014d8203bb8504a817c800830f42408080b8f9606060405260008054600160a060020a0319163317905560d78060226000396000f36060604052361560275760e060020a600035046341c0e1b58114606e578063e5225381146096575b60d56000341115606c57346060908152605890600160a060020a033316907f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a1590602090a35b565b60d5600054600160a060020a0390811633919091161415606c57600054600160a060020a0316ff5b60d5600054600160a060020a0390811633919091161415606c5760008054600160a060020a039081169190301631606082818181858883f15050505050565b001ca0e97321e5cdb2d677b840337568b057cdf9748659f47fc92e3d29c5d418c36f01a02c2eb384060b1784c0c5b4029d704bc75ae9cbcd17994db535e2cc4863ec1973", + "0xdd2294fae0bce9409e3f52684b9947613147b9fe2fcb8efb648ca096728236f5"); + // clang-format on +} + +BOOST_AUTO_TEST_CASE(handleEIP1559TxTest) +{ + // method eth_sendRawTransaction + auto testEIP1559Tx = [&](std::string const& rawTx, std::string const& expectHash) { + // clang-format off + const std::string request = R"({"jsonrpc":"2.0","id":1132123, "method":"eth_sendRawTransaction","params":[")" + rawTx + R"("]})"; + // clang-format on + auto rawTxBytes = fromHexWithPrefix(rawTx); + auto rawTxBytesRef = bcos::ref(rawTxBytes); + Web3Transaction rawWeb3Tx; + codec::rlp::decode(rawTxBytesRef, rawWeb3Tx); + auto response = onRPCRequestWrapper(request); + validRespCheck(response); + BOOST_CHECK(response["id"].asInt64() == 1132123); + BOOST_CHECK(response["result"].asString() == expectHash); + std::vector hashes = {HashType(expectHash)}; + task::wait([&rawWeb3Tx]( + Web3TestFixture* self, decltype(hashes) m_hashes) -> task::Task { + auto txs = co_await self->txPool->getTransactions(m_hashes); + BOOST_CHECK(txs.size() == 1); + BOOST_CHECK(txs[0]->hash() == m_hashes[0]); + BOOST_CHECK(txs[0]->type() == 1); + BOOST_CHECK(!txs[0]->extraTransactionBytes().empty()); + auto ref = bytesRef(const_cast(txs[0]->extraTransactionBytes().data()), + txs[0]->extraTransactionBytes().size()); + Web3Transaction tx; + bcos::codec::rlp::decode(ref, tx); + BOOST_CHECK(tx.type == rawWeb3Tx.type); + BOOST_CHECK(tx.data == rawWeb3Tx.data); + BOOST_CHECK(tx.nonce == rawWeb3Tx.nonce); + BOOST_CHECK(tx.to == rawWeb3Tx.to); + BOOST_CHECK(tx.value == rawWeb3Tx.value); + BOOST_CHECK(tx.maxFeePerGas == rawWeb3Tx.maxFeePerGas); + BOOST_CHECK(tx.maxPriorityFeePerGas == rawWeb3Tx.maxPriorityFeePerGas); + BOOST_CHECK(tx.gasLimit == rawWeb3Tx.gasLimit); + BOOST_CHECK(tx.accessList == rawWeb3Tx.accessList); + BOOST_CHECK(tx.chainId == rawWeb3Tx.chainId); + }(this, std::move(hashes))); + return rawWeb3Tx; + }; + + // clang-format off + // https://etherscan.io/tx/0x5b2f242c755ec9f9ed36628331991bd6c90b712b5867a0c7f3a4516caf09cc68 + testEIP1559Tx( + "0x02f871018308b3e6808501cd2ec1d7826ac194ba1951df0c0a52af23857c5ab48b4c43a57e7ed1872700f2d0ba3db080c001a069be171dfa805790a28f1bfcd131eb2aa8f345f601c4a3659de4ae8d624a7b89a06e0f6ed7d035397547aeac0e5130847570f4b607350f71c1391b7cb7f9dd604c", + "0x5b2f242c755ec9f9ed36628331991bd6c90b712b5867a0c7f3a4516caf09cc68"); + // https://etherscan.io/tx/0x698f4dc5e9948d62733d84c4adce663e06fe9c2a42c1bce9dc675fc0ae66cfdc + testEIP1559Tx("0x02f8b00181c4830ecd1085026856b07782b744944c9edd5852cd905f086c759e8383e09bff1e68b380b844095ea7b3000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc080a0170f981789f3265ec823403335db3c9da85a191720417b9d29253cbd56e6a3c9a05f8dd3422141cc9c158a776069769fd6b6ac35d1a9c6a2b3d3722b28fd5f9759", + "0x698f4dc5e9948d62733d84c4adce663e06fe9c2a42c1bce9dc675fc0ae66cfdc"); + // https://etherscan.io/tx/0x91d638aec203ed00f60ac4b89916d6a79532a6acc24088286891c978d66106ce + testEIP1559Tx("0x02f8b3018310468983e4e1c085746a528800830249f094a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4880b844a9059cbb000000000000000000000000a4909347fe9cfd582a382253fc4a5710d32c016b0000000000000000000000000000000000000000000000000000000042968240c080a06d687ed8689c5f022c97cb4be0053d01e2d7c03236fc4bc5765169a29600fa2ca023bee5a2365088addf6ac5844d6ccfebdeddb979165aaa7371813eb1b3a89e37", + "0x91d638aec203ed00f60ac4b89916d6a79532a6acc24088286891c978d66106ce"); + // clang-format on +} + +BOOST_AUTO_TEST_CASE(handleEIP4844TxTest) +{ + // method eth_sendRawTransaction + auto testEIP4844Tx = [&](std::string const& rawTx, std::string const& expectHash) { + // clang-format off + const std::string request = R"({"jsonrpc":"2.0","id":1132123, "method":"eth_sendRawTransaction","params":[")" + rawTx + R"("]})"; + // clang-format on + auto rawTxBytes = fromHexWithPrefix(rawTx); + auto rawTxBytesRef = bcos::ref(rawTxBytes); + Web3Transaction rawWeb3Tx; + codec::rlp::decode(rawTxBytesRef, rawWeb3Tx); + auto response = onRPCRequestWrapper(request); + validRespCheck(response); + BOOST_CHECK(response["id"].asInt64() == 1132123); + BOOST_CHECK(response["result"].asString() == expectHash); + std::vector hashes = {HashType(expectHash)}; + task::wait([&rawWeb3Tx]( + Web3TestFixture* self, decltype(hashes) m_hashes) -> task::Task { + auto txs = co_await self->txPool->getTransactions(m_hashes); + BOOST_CHECK(txs.size() == 1); + BOOST_CHECK(txs[0]->hash() == m_hashes[0]); + BOOST_CHECK(txs[0]->type() == 1); + BOOST_CHECK(!txs[0]->extraTransactionBytes().empty()); + auto ref = bytesRef(const_cast(txs[0]->extraTransactionBytes().data()), + txs[0]->extraTransactionBytes().size()); + Web3Transaction tx; + bcos::codec::rlp::decode(ref, tx); + BOOST_CHECK(tx.type == rawWeb3Tx.type); + BOOST_CHECK(tx.data == rawWeb3Tx.data); + BOOST_CHECK(tx.nonce == rawWeb3Tx.nonce); + BOOST_CHECK(tx.to == rawWeb3Tx.to); + BOOST_CHECK(tx.value == rawWeb3Tx.value); + BOOST_CHECK(tx.maxFeePerGas == rawWeb3Tx.maxFeePerGas); + BOOST_CHECK(tx.maxPriorityFeePerGas == rawWeb3Tx.maxPriorityFeePerGas); + BOOST_CHECK(tx.gasLimit == rawWeb3Tx.gasLimit); + BOOST_CHECK(tx.accessList == rawWeb3Tx.accessList); + BOOST_CHECK(tx.chainId == rawWeb3Tx.chainId); + BOOST_CHECK(tx.maxFeePerBlobGas == rawWeb3Tx.maxFeePerBlobGas); + BOOST_CHECK(tx.blobVersionedHashes == rawWeb3Tx.blobVersionedHashes); + }(this, std::move(hashes))); + return rawWeb3Tx; + }; + + // clang-format off + // https://etherscan.io/tx/0x637b7e88d7a0992b62a973acd41736ee2b63be1c86d0ffeb683747964daea892 + testEIP4844Tx( + "0x03f902fd018309a7ee8405f5e1008522ecb25c008353ec6094c662c410c0ecf747543f5ba90660f6abebd9c8c480b90264b72d42a100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000d06ad5334c4498113f3cc1548635e7c32bb72562421fddef1e60d165d8035ebe2031fe8a2addb2bec3c5c43b6168e3dac21f84cddbfae7d9e24977fbee8038772000000000000000000000000000000000000000000000000000000000009a7ed04ef432aa69fe0dd81c5d5d07464120008bbaede9cf73c4ca149f0be56acfbf605ba2078240f1585f96424c2d1ee48211da3b3f9177bf2b9880b4fc91d59e9a200000000000000000000000000000000000000000000000000000000000000010000000000000000df5459e0fefcbcfb5585179c4dfde48bc334f5836ad2821f0000000000000000883e48453eb072b0e184b27522f52d8324ee5d6ee778b05002c8df8c56bb3f47ab53b12be77ca1192abf4821a3e2b32c24d34af793fbcdca00000000000000000000000000000000cf420df13fc18924e41f7b0bd34de6a6000000000000000000000000000000000f389c6a65227d0e916c9670e8398e0d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003088cf8a7c3410fc132c573b94603dd2cf6e25555808a487c925ae595f6e2d1907907ba57a3fb42729eb1de453ebeda6bc00000000000000000000000000000000c08522ecb25c00e1a00131e940049304bb30d0da71fcccc542712238c5e2aa6ad96cf7eb5fa8ef974880a0f9ea6a59f3f160f36e1e2248085ec9d3bc3797462b4e5fd2c53f6753ddc88cc3a06e0aa62f613475a881564eb9269762b466560d4870835a198e12f6d5e05a9eaa", + "0x637b7e88d7a0992b62a973acd41736ee2b63be1c86d0ffeb683747964daea892"); + // https://etherscan.io/tx/0x82d366c93c5fb2cbe8f1b6f0f19c260d11a958875560a398d75784ce9f4d6adf + testEIP4844Tx("0x03f9013b018310fcdb847735940085041e2a063282520894ff000000000000000000000000000000000000108080c0843b9aca00f8c6a001a74d77682287763fc9a460f1c0a2f686daa8bdbb8757564c0fc05f54bbd6d5a0015d0db22bb787710c9d112373a0a49132646f0813c478d15ed247c148982172a00185ece9183ab795a0bfd3326d5b64848d9839992ac3f3ce4a55afb6e7ba4520a0013d1e438e3fb85830cb49bad9598a1ac720b778796c67a00ed8e20eebde05c5a001ff9dae3fb9320795caa1b474a9f667f2022e0e3d8a678ebfe163d7801908c1a001622f10864730dcd603857f00824ad18e070c64b57f9878738db1f85cfad33880a04539a1636f5c431241be963c898bc59491ed7c01853b75ed9e9e9bc47d0aa9ada031880afe746833518e6ea271ecd6a3943fa6da09ba1f889531729a1c8270e7cc", + "0x82d366c93c5fb2cbe8f1b6f0f19c260d11a958875560a398d75784ce9f4d6adf"); + // https://etherscan.io/tx/0xa8fd95a70b4f2b6cea8c52bcb782b5c3f806a0d5250cf75a1d97a6e899f09979 + testEIP4844Tx("0x03f9013a0182a8f98477359400850432ec4812825208946f54ca6f6ede96662024ffd61bfd18f3f4e34dff8080c0843b9aca00f8c6a001fb60d5b0abeff9e3d47099386a31eed62cd55d54aa146b42d10eb81b0a9b2aa00156b2193030eb7c9496d8586492934a57a5ebd0fac816ef1ea01dc4d09498c5a001bd78704a4b015adec86a654a123045312b083641ababec0e60ef17be4453e7a001b59b9a8b8d35bd6afcff91c6afc56ebcaf4a62f6e83f5e57aac4484f022cc4a00110aac506aff4957e40d4640f0763015b84bbb8c23f50f1b13d501067bea878a001f100a97a70563cd623e588c976155ffe5999d1e9258302d969964d7524bd0280a08c1cff27365c5fe0a6ebfe5e010b3460747489302547f3ba5b181390fad485ada02af2f430e0dfad48ba78853b59486ea7425835c3a7034bb22702234f8994288f", + "0xa8fd95a70b4f2b6cea8c52bcb782b5c3f806a0d5250cf75a1d97a6e899f09979"); + // clang-format on +} BOOST_AUTO_TEST_SUITE_END() } // namespace bcos::test \ No newline at end of file diff --git a/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp b/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp index 5585cdad26..6b92e36407 100644 --- a/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp +++ b/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp @@ -69,6 +69,8 @@ BOOST_AUTO_TEST_CASE(testLegacyTransactionDecode) toHex(tx.signatureS), "2d690516512020171c1ec870f6ff45398cc8609250326be89915fb538e7bd718"); BOOST_CHECK_EQUAL(tx.getSignatureV(), tx.chainId.value() * 2 + 35 + 1); + auto hash = tx.hash().hexPrefixed(); + BOOST_CHECK_EQUAL(hash, bcos::crypto::keccak256Hash(ref(bytes)).hexPrefixed()); bcos::bytes encoded{}; codec::rlp::encode(encoded, tx); auto rawTx2 = toHexStringWithPrefix(encoded); @@ -234,6 +236,8 @@ BOOST_AUTO_TEST_CASE(EIP1559Recover) BOOST_CHECK_EQUAL(tx.getSignatureV(), tx.chainId.value() * 2 + 35); BOOST_CHECK_EQUAL(tx.hash().hexPrefixed(), "0xcf6b53ec88659fc86e854af2e8453fa519ca261f949ef291e33c5f44ead870dc"); + auto txHash = bcos::crypto::keccak256Hash(ref(bytes)).hexPrefixed(); + BOOST_CHECK_EQUAL(txHash, "0xcf6b53ec88659fc86e854af2e8453fa519ca261f949ef291e33c5f44ead870dc"); bcos::bytes encoded{}; codec::rlp::encode(encoded, tx); auto rawTx2 = toHexStringWithPrefix(encoded); diff --git a/bcos-utilities/bcos-utilities/DataConvertUtility.h b/bcos-utilities/bcos-utilities/DataConvertUtility.h index 738e4245e3..e87b497578 100644 --- a/bcos-utilities/bcos-utilities/DataConvertUtility.h +++ b/bcos-utilities/bcos-utilities/DataConvertUtility.h @@ -35,6 +35,7 @@ namespace bcos { template + requires RANGES::range && RANGES::sized_range Out toHex(const Binary& binary, std::string_view prefix = std::string_view()) { Out out; @@ -49,6 +50,14 @@ Out toHex(const Binary& binary, std::string_view prefix = std::string_view()) return out; } +template +Out toHex(std::unsigned_integral auto number, std::string_view prefix = std::string_view()) +{ + std::basic_string bytes(8, '\0'); + boost::endian::store_big_u64(bytes.data(), number); + return toHex(bytes, prefix); +} + template concept Binary = RANGES::contiguous_range; static std::string toQuantity(const Binary auto& binary) @@ -59,7 +68,7 @@ static std::string toQuantity(const Binary auto& binary) } auto&& hex = toHex(binary); auto it = hex.begin(); - while (it != hex.end()) + while ((it + 1) != hex.end()) { if (*it != '0') { From 23a2fcc9659b3b4bb3f70db037108e904b92d8d1 Mon Sep 17 00:00:00 2001 From: kyonRay Date: Wed, 17 Apr 2024 20:40:25 +0800 Subject: [PATCH 13/39] (rpc): fix get system config. --- .../web3jsonrpc/endpoints/EthEndpoint.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp index fe6d32b5f6..74825415c4 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp @@ -81,9 +81,18 @@ task::Task EthEndpoint::gasPrice(const Json::Value&, Json::Value& response // result: gasPrice(QTY) auto ledger = m_nodeService->ledger(); // TODO: to check gas price is hex value or not - auto [gasPrice, _] = co_await ledger::getSystemConfig(*ledger, ledger::SYSTEM_KEY_TX_GAS_PRICE); - auto value = std::stoull(gasPrice); - Json::Value result = toQuantity(value); + auto config = co_await ledger::getSystemConfig(*ledger, ledger::SYSTEM_KEY_TX_GAS_PRICE); + Json::Value result; + if (config.has_value()) + { + auto [gasPrice, _] = config.value(); + auto const value = std::stoull(gasPrice); + result = toQuantity(value); + } + else + { + result = "0x5208"; // 21000 + } buildJsonContent(result, response); co_return; } From 62e3d7270c7eb636c457b2670abf79e8f4196841 Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:21:19 +0800 Subject: [PATCH 14/39] (rpc,ledger): impl getStorageAt in rpc and ledger. (#4380) --- bcos-executor/test/unittest/mock/MockLedger.h | 5 + bcos-framework/bcos-framework/ledger/Ledger.h | 9 + .../bcos-framework/ledger/LedgerInterface.h | 14 ++ .../testutils/faker/FakeLedger.h | 6 + bcos-ledger/src/libledger/Ledger.cpp | 43 ++++ bcos-ledger/src/libledger/Ledger.h | 4 + bcos-ledger/src/libledger/LedgerMethods.cpp | 44 ++++ bcos-ledger/src/libledger/LedgerMethods.h | 3 + .../web3jsonrpc/endpoints/EthEndpoint.cpp | 202 ++++++++++++------ .../web3jsonrpc/model/Web3Transaction.h | 28 +-- bcos-scheduler/test/mock/MockLedger3.h | 6 + .../client/LedgerServiceClient.h | 6 + .../bcos-utilities/DataConvertUtility.h | 16 +- 13 files changed, 308 insertions(+), 78 deletions(-) diff --git a/bcos-executor/test/unittest/mock/MockLedger.h b/bcos-executor/test/unittest/mock/MockLedger.h index b8e7e01e61..b9b3875536 100644 --- a/bcos-executor/test/unittest/mock/MockLedger.h +++ b/bcos-executor/test/unittest/mock/MockLedger.h @@ -39,6 +39,11 @@ class MockLedger : public bcos::ledger::LedgerInterface } _callback(nullptr); } + void asyncGetStorageAt(std::string_view, std::string_view, protocol::BlockNumber, + std::function _onGetStorage) override + { + _onGetStorage(nullptr, ""); + } bcos::Error::Ptr storeTransactionsAndReceipts( bcos::protocol::ConstTransactionsPtr, bcos::protocol::Block::ConstPtr) override diff --git a/bcos-framework/bcos-framework/ledger/Ledger.h b/bcos-framework/bcos-framework/ledger/Ledger.h index dea51f5cba..62724811b9 100644 --- a/bcos-framework/bcos-framework/ledger/Ledger.h +++ b/bcos-framework/bcos-framework/ledger/Ledger.h @@ -141,6 +141,15 @@ inline constexpr struct GetTransactions } } getTransactions{}; +inline constexpr struct GetStorageAt +{ + task::Task operator()(auto& ledger, std::string_view address, std::string_view key, + bcos::protocol::BlockNumber number) const + { + co_return co_await tag_invoke(*this, ledger, address, key, number); + } +} getStorageAt{}; + template using tag_t = std::decay_t; } // namespace bcos::ledger diff --git a/bcos-framework/bcos-framework/ledger/LedgerInterface.h b/bcos-framework/bcos-framework/ledger/LedgerInterface.h index 816ec0cbd4..e2525b1c3b 100644 --- a/bcos-framework/bcos-framework/ledger/LedgerInterface.h +++ b/bcos-framework/bcos-framework/ledger/LedgerInterface.h @@ -168,6 +168,20 @@ class LedgerInterface virtual void asyncPreStoreBlockTxs(bcos::protocol::ConstTransactionsPtr _blockTxs, bcos::protocol::Block::ConstPtr block, std::function _callback) = 0; + + /** + * @brief Async get storage value by address and key and block number. It will access the table + * of address like /apps/[address] and get the value of key. NOTE: blockNumber is ignored + * nowadays, it will always get the latest value of key in address. + * @param _address the address of contract/EOA. if in EVM, it should be the address of contract, + * hex string; if in WASM, it should be the path name of contract. + * @param _key the key of storage + * @param _blockNumber the block number to get the storage value + * @param _onGetStorage callback when get storage value, + */ + virtual void asyncGetStorageAt(std::string_view _address, std::string_view _key, + protocol::BlockNumber _blockNumber, + std::function _onGetStorage) = 0; }; } // namespace bcos::ledger diff --git a/bcos-framework/bcos-framework/testutils/faker/FakeLedger.h b/bcos-framework/bcos-framework/testutils/faker/FakeLedger.h index 0894d7a4a0..4384f297e7 100644 --- a/bcos-framework/bcos-framework/testutils/faker/FakeLedger.h +++ b/bcos-framework/bcos-framework/testutils/faker/FakeLedger.h @@ -162,6 +162,12 @@ class FakeLedger : public LedgerInterface, public std::enable_shared_from_this _onGetStorage) override + { + _onGetStorage(nullptr, ""); + } + // the txpool module use this interface to store txs bcos::Error::Ptr storeTransactionsAndReceipts(bcos::protocol::ConstTransactionsPtr blockTxs, bcos::protocol::Block::ConstPtr block) override diff --git a/bcos-ledger/src/libledger/Ledger.cpp b/bcos-ledger/src/libledger/Ledger.cpp index 2d0bb3addf..423b6d2ff7 100644 --- a/bcos-ledger/src/libledger/Ledger.cpp +++ b/bcos-ledger/src/libledger/Ledger.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -123,6 +124,48 @@ void Ledger::asyncPreStoreBlockTxs(bcos::protocol::ConstTransactionsPtr _blockTx _callback(nullptr); } +void Ledger::asyncGetStorageAt(std::string_view _address, std::string_view _key, + protocol::BlockNumber _blockNumber, std::function _onGetStorage) +{ + auto contractTableName = getContractTableName(SYS_DIRECTORY::USER_APPS, _address); + // TODO: blockNumber is not used nowadays + std::ignore = _blockNumber; + LEDGER_LOG(TRACE) << LOG_DESC("asyncGetStorageAt") << LOG_KV("address", _address) + << LOG_KV("key", _key) << LOG_KV("blockNumber", _blockNumber); + m_storage->asyncGetRow(contractTableName, _key, + [onGetStorage = std::move(_onGetStorage)](auto&& _error, auto&& _entry) { + if (_error) + { + onGetStorage(std::move(_error), ""); + return; + } + if (!_entry) + { + onGetStorage( + BCOS_ERROR_PTR(GetStorageError, "Cannot get storage in address and key"), ""); + return; + } + auto value = _entry->exportFields(); + std::visit( + [onGetStorage = std::move(onGetStorage)](auto&& valueInside) { + std::string realValue{}; + if constexpr (concepts::PointerLike) + { + realValue = std::string( + reinterpret_cast(valueInside->data()), valueInside->size()); + } + else + { + realValue = std::string( + reinterpret_cast(valueInside.data()), valueInside.size()); + } + onGetStorage(nullptr, std::move(realValue)); + }, + value); + }); +} + + void Ledger::asyncPrewriteBlock(bcos::storage::StorageInterface::Ptr storage, bcos::protocol::ConstTransactionsPtr _blockTxs, bcos::protocol::Block::ConstPtr block, std::function callback, diff --git a/bcos-ledger/src/libledger/Ledger.h b/bcos-ledger/src/libledger/Ledger.h index 9d2307293b..cb995bee27 100644 --- a/bcos-ledger/src/libledger/Ledger.h +++ b/bcos-ledger/src/libledger/Ledger.h @@ -111,6 +111,10 @@ class Ledger : public LedgerInterface std::function&&)> _callback) override; + void asyncGetStorageAt(std::string_view _address, std::string_view _key, + protocol::BlockNumber _blockNumber, + std::function _onGetStorage) override; + bool buildGenesisBlock(GenesisConfig const& genesis, ledger::LedgerConfig const& ledgerConfig); void asyncGetBlockTransactionHashes(bcos::protocol::BlockNumber blockNumber, diff --git a/bcos-ledger/src/libledger/LedgerMethods.cpp b/bcos-ledger/src/libledger/LedgerMethods.cpp index e5df4a1a34..d7dea16b03 100644 --- a/bcos-ledger/src/libledger/LedgerMethods.cpp +++ b/bcos-ledger/src/libledger/LedgerMethods.cpp @@ -552,3 +552,47 @@ bcos::task::Task bcos::ledger::tag_invoke( Awaitable awaitable{.m_ledger = ledger, .m_hashes = std::move(hashes), .m_result = {}}; co_return co_await awaitable; } + +bcos::task::Task bcos::ledger::tag_invoke(ledger::tag_t, + LedgerInterface& ledger, std::string_view address, std::string_view key, + bcos::protocol::BlockNumber number) +{ + struct Awaitable + { + bcos::ledger::LedgerInterface& m_ledger; + std::string_view m_address; + std::string_view m_key; + bcos::protocol::BlockNumber m_number; + + std::variant m_result; + + constexpr static bool await_ready() noexcept { return false; } + void await_suspend(CO_STD::coroutine_handle<> handle) + { + m_ledger.asyncGetStorageAt( + m_address, m_key, m_number, [this, handle](auto&& error, auto&& value) { + if (error) + { + m_result.emplace(std::move(error)); + } + else + { + m_result.emplace(value); + } + handle.resume(); + }); + } + std::string await_resume() + { + if (std::holds_alternative(m_result)) + { + BOOST_THROW_EXCEPTION(*std::get(m_result)); + } + return std::get(m_result); + } + }; + + Awaitable awaitable{ + .m_ledger = ledger, .m_address = address, .m_key = key, .m_number = number, .m_result = {}}; + co_return co_await awaitable; +} diff --git a/bcos-ledger/src/libledger/LedgerMethods.h b/bcos-ledger/src/libledger/LedgerMethods.h index b5175ec65d..34ac88312a 100644 --- a/bcos-ledger/src/libledger/LedgerMethods.h +++ b/bcos-ledger/src/libledger/LedgerMethods.h @@ -95,4 +95,7 @@ task::Task tag_invoke( task::Task tag_invoke( ledger::tag_t, LedgerInterface& ledger, crypto::HashListPtr hashes); +task::Task tag_invoke(ledger::tag_t, LedgerInterface& ledger, + std::string_view address, std::string_view key, bcos::protocol::BlockNumber number); + } // namespace bcos::ledger \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp index 74825415c4..cc612cf3e4 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp @@ -22,7 +22,6 @@ #include #include -#include #include #include #include @@ -114,35 +113,74 @@ task::Task EthEndpoint::getBalance(const Json::Value& request, Json::Value { // params: address(DATA), blockNumber(QTY|TAG) // result: balance(QTY) - // TODO: get balance from ledger - Json::Value result = "0xfffffffffffffff"; + auto const address = toView(request[0u]); + // TODO)): blockNumber is ignored nowadays + // auto const blockTag = toView(request[1u]); + // auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); + auto const ledger = m_nodeService->ledger(); + u256 balance; + try + { + auto const value = co_await ledger::getStorageAt( + *ledger, address, bcos::executor::ACCOUNT_BALANCE, /*blockNumber*/ 0); + balance = u256(value); + } + catch (...) + { + WEB3_LOG(TRACE) << LOG_DESC("getBalance failed, return 0 by defualt") + << LOG_KV("address", address); + balance = 0; + } + Json::Value result = toQuantity(balance); buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::getStorageAt(const Json::Value&, Json::Value&) +task::Task EthEndpoint::getStorageAt(const Json::Value& request, Json::Value& response) { // params: address(DATA), position(QTY), blockNumber(QTY|TAG) // result: value(DATA) - BOOST_THROW_EXCEPTION( - JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); + auto const address = toView(request[0u]); + auto const position = toView(request[1u]); + // TODO)): blockNumber is ignored nowadays + // auto const blockTag = toView(request[2u]); + // auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); + auto const ledger = m_nodeService->ledger(); + auto const value = co_await ledger::getStorageAt(*ledger, address, position, /*blockNumber*/ 0); + Json::Value result = value; + buildJsonContent(result, response); co_return; } task::Task EthEndpoint::getTransactionCount(const Json::Value& request, Json::Value& response) { // params: address(DATA), blockNumber(QTY|TAG) // result: nonce(QTY) - // TODO: impliment getTransactionCount - static thread_local std::mt19937 generator(std::random_device{}()); - std::uniform_int_distribution dis(0, 255); - std::array randomFixedBytes; - for (auto& element : randomFixedBytes) + auto const address = toView(request[0u]); + // TODO)): blockNumber is ignored nowadays + // auto const blockTag = toView(request[1u]); + // auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); + auto const ledger = m_nodeService->ledger(); + uint64_t nonce = 0; + try { - element = dis(generator); + auto const value = co_await ledger::getStorageAt( + *ledger, address, bcos::executor::ACCOUNT_NONCE, /*blockNumber*/ 0); + nonce = std::stoull(value); } - - uint16_t* firstNum = (uint16_t*)randomFixedBytes.data(); - // uint64_t* lastNum = (uint64_t*)(randomFixedBytes.data() + sizeof(uint64_t)); - Json::Value result = toQuantity(*firstNum); + catch (...) + { + WEB3_LOG(TRACE) << LOG_DESC("get address nonce failed, return random value by defualt") + << LOG_KV("address", address); + static thread_local std::mt19937 generator(std::random_device{}()); + std::uniform_int_distribution dis(0, 255); + std::array randomFixedBytes; + for (auto& element : randomFixedBytes) + { + element = dis(generator); + } + const auto* firstNum = (uint32_t*)randomFixedBytes.data(); + nonce = *firstNum; + } + Json::Value result = toQuantity(nonce); buildJsonContent(result, response); co_return; } @@ -186,12 +224,50 @@ task::Task EthEndpoint::getUncleCountByBlockNumber( buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::getCode(const Json::Value&, Json::Value&) +task::Task EthEndpoint::getCode(const Json::Value& request, Json::Value& response) { // params: address(DATA), blockNumber(QTY|TAG) // result: code(DATA) - BOOST_THROW_EXCEPTION( - JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); + auto const address = toView(request[0u]); + // TODO)): blockNumber is ignored nowadays + // auto const blockTag = toView(request[1u]); + // auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); + auto const scheduler = m_nodeService->scheduler(); + struct Awaitable + { + bcos::scheduler::SchedulerInterface::Ptr m_scheduler; + std::string_view m_address; + std::variant m_result{}; + constexpr static bool await_ready() noexcept { return false; } + void await_suspend(CO_STD::coroutine_handle<> handle) noexcept + { + m_scheduler->getCode(m_address, [this, handle](auto&& error, auto&& code) { + if (error) + { + m_result.emplace(std::move(error)); + } + else + { + m_result.emplace(std::move(code)); + } + handle.resume(); + }); + } + bcos::bytes await_resume() noexcept + { + if (std::holds_alternative(m_result)) + { + BOOST_THROW_EXCEPTION(*std::get(m_result)); + } + return std::move(std::get(m_result)); + } + }; + auto const code = co_await Awaitable{ + .m_scheduler = scheduler, + .m_address = address, + }; + Json::Value result = toHexStringWithPrefix(code); + buildJsonContent(result, response); co_return; } task::Task EthEndpoint::sign(const Json::Value&, Json::Value& response) @@ -341,7 +417,7 @@ task::Task EthEndpoint::estimateGas(const Json::Value& request, Json::Valu { // params: transaction(TX), blockNumber(QTY|TAG) // result: gas(QTY) - Json::Value result = "0x0"; + Json::Value result = "0x1"; buildJsonContent(result, response); co_return; } @@ -349,10 +425,10 @@ task::Task EthEndpoint::getBlockByHash(const Json::Value& request, Json::V { // params: blockHash(DATA), fullTransaction(Boolean) // result: block(BLOCK) - auto blockHash = toView(request[0u]); - auto fullTransaction = request[1u].asBool(); + auto const blockHash = toView(request[0u]); + auto const fullTransaction = request[1u].asBool(); auto const ledger = m_nodeService->ledger(); - auto number = co_await ledger::getBlockNumber( + auto const number = co_await ledger::getBlockNumber( *ledger, crypto::HashType(blockHash, crypto::HashType::FromHex)); auto block = co_await ledger::getBlockData(*ledger, number, bcos::ledger::FULL_BLOCK); Json::Value result = Json::objectValue; @@ -364,8 +440,8 @@ task::Task EthEndpoint::getBlockByNumber(const Json::Value& request, Json: { // params: blockNumber(QTY|TAG), fullTransaction(Boolean) // result: block(BLOCK) - auto blockTag = toView(request[0u]); - auto fullTransaction = request[1u].asBool(); + auto const blockTag = toView(request[0u]); + auto const fullTransaction = request[1u].asBool(); auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); auto const ledger = m_nodeService->ledger(); auto block = co_await ledger::getBlockData(*ledger, blockNumber, bcos::ledger::FULL_BLOCK); @@ -379,12 +455,12 @@ task::Task EthEndpoint::getTransactionByHash( { // params: transactionHash(DATA) // result: transaction(TX) - auto txHash = toView(request[0u]); - auto hash = crypto::HashType(txHash, crypto::HashType::FromHex); + auto const txHash = toView(request[0u]); + auto const hash = crypto::HashType(txHash, crypto::HashType::FromHex); auto hashList = std::make_shared(); hashList->push_back(hash); auto const ledger = m_nodeService->ledger(); - auto txs = co_await ledger::getTransactions(*ledger, std::move(hashList)); + auto const txs = co_await ledger::getTransactions(*ledger, std::move(hashList)); auto receipt = co_await ledger::getReceipt(*ledger, hash); if (!receipt || !txs || txs->empty()) { @@ -403,26 +479,26 @@ task::Task EthEndpoint::getTransactionByBlockHashAndIndex( { // params: blockHash(DATA), transactionIndex(QTY) // result: transaction(TX) - // auto blockHash = toView(request[0u]); - // auto transactionIndex = fromQuantity(std::string(toView(request[1u]))); - // auto hash = crypto::HashType(blockHash, crypto::HashType::FromHex); - // auto const ledger = m_nodeService->ledger(); - // auto number = co_await ledger::getBlockNumber(*ledger, hash); - // if (number <= 0) [[unlikely]] - // { - // BOOST_THROW_EXCEPTION( - // JsonRpcException(InvalidParams, "Invalid block hash: " + hash.hexPrefixed())); - // } - // auto block = co_await ledger::getBlockData( - // *ledger, number, bcos::ledger::TRANSACTIONS | bcos::ledger::HEADER); - // if (!block || transactionIndex >= block->transactionsSize()) [[unlikely]] - // { - // BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParams, "Invalid transaction index!")); - // } - // auto tx = block->transaction(transactionIndex); - // Json::Value result = Json::objectValue; - // combineTxResponse(result, std::move(tx), nullptr, std::move(block)); - // buildJsonContent(result, response); + auto const blockHash = toView(request[0u]); + auto const transactionIndex = fromQuantity(std::string(toView(request[1u]))); + auto const hash = crypto::HashType(blockHash, crypto::HashType::FromHex); + auto const ledger = m_nodeService->ledger(); + auto const number = co_await ledger::getBlockNumber(*ledger, hash); + if (number <= 0) [[unlikely]] + { + BOOST_THROW_EXCEPTION( + JsonRpcException(InvalidParams, "Invalid block hash: " + hash.hexPrefixed())); + } + auto block = co_await ledger::getBlockData( + *ledger, number, bcos::ledger::TRANSACTIONS | bcos::ledger::HEADER); + if (!block || transactionIndex >= block->transactionsSize()) [[unlikely]] + { + BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParams, "Invalid transaction index!")); + } + auto tx = block->transaction(transactionIndex); + Json::Value result = Json::objectValue; + combineTxResponse(result, std::move(tx), nullptr, std::move(block)); + buildJsonContent(result, response); co_return; } task::Task EthEndpoint::getTransactionByBlockNumberAndIndex( @@ -430,20 +506,20 @@ task::Task EthEndpoint::getTransactionByBlockNumberAndIndex( { // params: blockNumber(QTY|TAG), transactionIndex(QTY) // result: transaction(TX) - // auto blockTag = toView(request[0u]); - // auto transactionIndex = fromQuantity(std::string(toView(request[1u]))); - // auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); - // auto const ledger = m_nodeService->ledger(); - // auto block = co_await ledger::getBlockData( - // *ledger, blockNumber, bcos::ledger::TRANSACTIONS | bcos::ledger::HEADER); - // if (!block || transactionIndex >= block->transactionsSize()) [[unlikely]] - // { - // BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParams, "Invalid transaction index!")); - // } - // auto tx = block->transaction(transactionIndex); - // Json::Value result = Json::objectValue; - // combineTxResponse(result, std::move(tx), nullptr, std::move(block)); - // buildJsonContent(result, response); + auto const blockTag = toView(request[0u]); + auto const transactionIndex = fromQuantity(std::string(toView(request[1u]))); + auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); + auto const ledger = m_nodeService->ledger(); + auto block = co_await ledger::getBlockData( + *ledger, blockNumber, bcos::ledger::TRANSACTIONS | bcos::ledger::HEADER); + if (!block || transactionIndex >= block->transactionsSize()) [[unlikely]] + { + BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParams, "Invalid transaction index!")); + } + auto tx = block->transaction(transactionIndex); + Json::Value result = Json::objectValue; + combineTxResponse(result, std::move(tx), nullptr, std::move(block)); + buildJsonContent(result, response); co_return; } task::Task EthEndpoint::getTransactionReceipt( @@ -452,7 +528,7 @@ task::Task EthEndpoint::getTransactionReceipt( // params: transactionHash(DATA) // result: transactionReceipt(RECEIPT) auto const hashStr = toView(request[0U]); - auto hash = crypto::HashType(hashStr, crypto::HashType::FromHex); + auto const hash = crypto::HashType(hashStr, crypto::HashType::FromHex); auto const ledger = m_nodeService->ledger(); auto receipt = co_await ledger::getReceipt(*ledger, hash); auto hashList = std::make_shared(); diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h index 2840efe113..5157c91f31 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h @@ -89,20 +89,20 @@ class Web3Transaction return signatureV + 27; } - // std::string sender() const - // { - // bcos::bytes sign{}; - // sign.reserve(crypto::SECP256K1_SIGNATURE_LEN); - // sign.insert(sign.end(), signatureR.begin(), signatureR.end()); - // sign.insert(sign.end(), signatureS.begin(), signatureS.end()); - // sign.push_back(signatureV); - // bcos::crypto::Keccak256 hashImpl; - // auto encodeForSign = encode(); - // auto hash = bcos::crypto::keccak256Hash(ref(encodeForSign)); - // const bcos::crypto::Secp256k1Crypto signatureImpl; - // auto [_, addr] = signatureImpl.recoverAddress(hashImpl, hash, ref(sign)); - // return toHexStringWithPrefix(addr); - // } + std::string sender() const + { + bcos::bytes sign{}; + sign.reserve(crypto::SECP256K1_SIGNATURE_LEN); + sign.insert(sign.end(), signatureR.begin(), signatureR.end()); + sign.insert(sign.end(), signatureS.begin(), signatureS.end()); + sign.push_back(signatureV); + bcos::crypto::Keccak256 hashImpl; + auto encodeForSign = encode(); + auto hash = bcos::crypto::keccak256Hash(ref(encodeForSign)); + const bcos::crypto::Secp256k1Crypto signatureImpl; + auto [_, addr] = signatureImpl.recoverAddress(hashImpl, hash, ref(sign)); + return toHexStringWithPrefix(addr); + } std::string toString() const noexcept { diff --git a/bcos-scheduler/test/mock/MockLedger3.h b/bcos-scheduler/test/mock/MockLedger3.h index 0a780ab1f2..e2b83605a3 100644 --- a/bcos-scheduler/test/mock/MockLedger3.h +++ b/bcos-scheduler/test/mock/MockLedger3.h @@ -169,6 +169,12 @@ class MockLedger3 : public bcos::ledger::LedgerInterface _onGetList) override {} + void asyncGetStorageAt(std::string_view, std::string_view, protocol::BlockNumber, + std::function _onGetStorage) override + { + _onGetStorage(nullptr, ""); + } + void asyncPreStoreBlockTxs(bcos::protocol::ConstTransactionsPtr _blockTxs, bcos::protocol::Block::ConstPtr block, std::function _callback) override diff --git a/bcos-tars-protocol/bcos-tars-protocol/client/LedgerServiceClient.h b/bcos-tars-protocol/bcos-tars-protocol/client/LedgerServiceClient.h index fe683f99d9..d61869081e 100644 --- a/bcos-tars-protocol/bcos-tars-protocol/client/LedgerServiceClient.h +++ b/bcos-tars-protocol/bcos-tars-protocol/client/LedgerServiceClient.h @@ -57,6 +57,12 @@ class LedgerServiceClient : public bcos::ledger::LedgerInterface BCOS_LOG(ERROR) << LOG_DESC("unimplemented method asyncPreStoreBlockTxs"); } + void asyncGetStorageAt(std::string_view, std::string_view, bcos::protocol::BlockNumber, + std::function) override + { + BCOS_LOG(ERROR) << LOG_DESC("unimplemented method asyncGetStorageAt"); + } + // TODO: implement this bcos::Error::Ptr storeTransactionsAndReceipts( bcos::protocol::ConstTransactionsPtr, bcos::protocol::Block::ConstPtr) override diff --git a/bcos-utilities/bcos-utilities/DataConvertUtility.h b/bcos-utilities/bcos-utilities/DataConvertUtility.h index e87b497578..babcaf84e9 100644 --- a/bcos-utilities/bcos-utilities/DataConvertUtility.h +++ b/bcos-utilities/bcos-utilities/DataConvertUtility.h @@ -83,7 +83,7 @@ static std::string toQuantity(const Binary auto& binary) } template -concept Number = std::is_integral_v || std::same_as; +concept Number = std::is_integral_v; static std::string toQuantity(Number auto number) { std::basic_string bytes(8, '\0'); @@ -91,6 +91,10 @@ static std::string toQuantity(Number auto number) return toQuantity(bytes); } +template +concept BigNumber = !std::is_integral_v && std::convertible_to; +static std::string toQuantity(BigNumber auto number); + template Out fromHex(const Hex& hex, std::string_view prefix = std::string_view()) { @@ -416,4 +420,14 @@ inline std::string toString(uint8_t const& _u) o << static_cast(_u); return o.str(); } + +std::string toQuantity(BigNumber auto number) +{ + if (number == 0) + { + return "0x0"; + } + return toCompactBigEndianString(number); +} + } // namespace bcos From d1e89e1ef820144e7c04d3b1ee54b235bb3d782f Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:30:56 +0800 Subject: [PATCH 15/39] (rpc,precompiled,ledger): add web3_chain genesis config, add web3_chain_id system config, fix getStorageAt logic error. (#4392) --- bcos-executor/src/executive/BlockContext.h | 2 + bcos-executor/src/executive/LedgerCache.h | 12 ++ .../precompiled/SystemConfigPrecompiled.cpp | 15 +++ bcos-executor/src/vm/EVMHostInterface.cpp | 2 +- bcos-executor/src/vm/HostContext.h | 3 + bcos-executor/test/unittest/mock/MockLedger.h | 10 +- .../bcos-framework/ledger/GenesisConfig.h | 3 + bcos-framework/bcos-framework/ledger/Ledger.h | 4 +- .../bcos-framework/ledger/LedgerConfig.h | 6 + .../bcos-framework/ledger/LedgerInterface.h | 14 ++- .../bcos-framework/ledger/LedgerTypeDef.h | 1 + .../bcos-framework/protocol/Protocol.h | 9 +- .../bcos-framework/protocol/Transaction.h | 14 +++ .../testutils/faker/FakeLedger.h | 8 +- bcos-ledger/src/libledger/Ledger.cpp | 53 +++------ bcos-ledger/src/libledger/Ledger.h | 34 +++++- bcos-ledger/src/libledger/LedgerMethods.cpp | 48 +------- bcos-ledger/src/libledger/LedgerMethods.h | 5 +- .../web3jsonrpc/endpoints/EthEndpoint.cpp | 110 +++++++++++++----- .../web3jsonrpc/model/BlockResponse.cpp | 21 ---- .../web3jsonrpc/model/ReceiptResponse.h | 71 ----------- .../web3jsonrpc/model/TransactionResponse.h | 41 ++++--- .../web3jsonrpc/model/Web3Transaction.cpp | 41 ++++--- .../web3jsonrpc/model/Web3Transaction.h | 8 +- bcos-rpc/bcos-rpc/web3jsonrpc/utils/Common.h | 1 + bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp | 6 +- bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp | 6 +- bcos-scheduler/test/mock/MockLedger3.h | 10 +- bcos-table/src/StateStorageFactory.h | 2 +- .../client/LedgerServiceClient.h | 6 - .../protocol/TransactionImpl.cpp | 9 +- .../bcos-tars-protocol/tars/Transaction.tars | 3 +- bcos-tool/bcos-tool/LedgerConfigFetcher.cpp | 18 +++ bcos-tool/bcos-tool/LedgerConfigFetcher.h | 1 + bcos-tool/bcos-tool/NodeConfig.cpp | 17 ++- bcos-tool/bcos-tool/NodeConfig.h | 1 + .../bcos-utilities/DataConvertUtility.h | 3 +- concepts/bcos-concepts/Serialize.h | 26 +++++ libinitializer/Initializer.cpp | 1 + tools/.ci/ci_check_air.sh | 34 +++--- tools/BcosAirBuilder/build_chain.sh | 4 + .../vm/EVMHostInterface.h | 2 +- .../vm/HostContext.h | 1 + 43 files changed, 381 insertions(+), 305 deletions(-) delete mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.cpp diff --git a/bcos-executor/src/executive/BlockContext.h b/bcos-executor/src/executive/BlockContext.h index 0764a51896..ce8a43d3f1 100644 --- a/bcos-executor/src/executive/BlockContext.h +++ b/bcos-executor/src/executive/BlockContext.h @@ -86,6 +86,8 @@ class BlockContext : public std::enable_shared_from_this VMSchedule const& vmSchedule() const { return m_schedule; } + LedgerCache::Ptr const& ledgerCache() const { return m_ledgerCache; } + ExecutiveFlowInterface::Ptr getExecutiveFlow(std::string codeAddress); void setExecutiveFlow(std::string codeAddress, ExecutiveFlowInterface::Ptr executiveFlow); diff --git a/bcos-executor/src/executive/LedgerCache.h b/bcos-executor/src/executive/LedgerCache.h index 9e3a67bd88..0b7ca44d7f 100644 --- a/bcos-executor/src/executive/LedgerCache.h +++ b/bcos-executor/src/executive/LedgerCache.h @@ -23,6 +23,8 @@ #include "../Common.h" #include "bcos-framework/ledger/LedgerInterface.h" +#include +#include #include #include @@ -102,6 +104,16 @@ class LedgerCache : public bcos::tool::LedgerConfigFetcher return txGasLimit; } + evmc_uint256be chainId() + { + if (ledgerConfig()->chainId().has_value()) + { + return ledgerConfig()->chainId().value(); + } + fetchChainId(); + return ledgerConfig()->chainId().value(); + } + private: std::map> m_blockNumber2Hash; mutable bcos::SharedMutex x_blockNumber2Hash; diff --git a/bcos-executor/src/precompiled/SystemConfigPrecompiled.cpp b/bcos-executor/src/precompiled/SystemConfigPrecompiled.cpp index 72be6718ba..d32399bf40 100644 --- a/bcos-executor/src/precompiled/SystemConfigPrecompiled.cpp +++ b/bcos-executor/src/precompiled/SystemConfigPrecompiled.cpp @@ -142,6 +142,21 @@ SystemConfigPrecompiled::SystemConfigPrecompiled(crypto::Hash::Ptr hashImpl) : P } return 0; })); + m_valueConverter.insert(std::make_pair( + SYSTEM_KEY_WEB3_CHAIN_ID, [](const std::string& _value, uint32_t blockVersion) -> uint64_t { + if (blockVersion < BlockVersion::V3_9_0_VERSION) + { + BOOST_THROW_EXCEPTION( + PrecompiledError(fmt::format("unsupported key {}", SYSTEM_KEY_WEB3_CHAIN_ID))); + } + if (!isNumStr(_value)) + { + BOOST_THROW_EXCEPTION(PrecompiledError( + fmt::format("Invalid value {}, the value for {} must be a number string.", + _value, SYSTEM_KEY_WEB3_CHAIN_ID))); + } + return 0; + })); } std::shared_ptr SystemConfigPrecompiled::call( diff --git a/bcos-executor/src/vm/EVMHostInterface.cpp b/bcos-executor/src/vm/EVMHostInterface.cpp index affe1ff404..24cd9f2ba3 100644 --- a/bcos-executor/src/vm/EVMHostInterface.cpp +++ b/bcos-executor/src/vm/EVMHostInterface.cpp @@ -216,10 +216,10 @@ evmc_tx_context getTxContext(evmc_host_context* _context) noexcept result.block_timestamp = hostContext.timestamp(); result.block_gas_limit = hostContext.blockGasLimit(); result.tx_gas_price = toEvmC(hostContext.gasPrice()); + result.chain_id = hostContext.chainId(); memset(result.block_coinbase.bytes, 0, 20); memset(result.block_prev_randao.bytes, 0, 32); - memset(result.chain_id.bytes, 0, 32); memset(result.block_base_fee.bytes, 0, 32); diff --git a/bcos-executor/src/vm/HostContext.h b/bcos-executor/src/vm/HostContext.h index 2384228118..a77ac9d3b6 100644 --- a/bcos-executor/src/vm/HostContext.h +++ b/bcos-executor/src/vm/HostContext.h @@ -27,6 +27,7 @@ #include "bcos-framework/protocol/BlockHeader.h" #include "bcos-framework/protocol/Protocol.h" #include "bcos-framework/storage/Table.h" +#include #include #include #include @@ -111,6 +112,8 @@ class HostContext : public evmc_host_context } } + evmc_uint256be chainId() const { return m_executive->blockContext().ledgerCache()->chainId(); } + /// Revert any changes made (by any of the other calls). void log(h256s&& _topics, bytesConstRef _data); diff --git a/bcos-executor/test/unittest/mock/MockLedger.h b/bcos-executor/test/unittest/mock/MockLedger.h index b9b3875536..f9f58b23fd 100644 --- a/bcos-executor/test/unittest/mock/MockLedger.h +++ b/bcos-executor/test/unittest/mock/MockLedger.h @@ -39,11 +39,6 @@ class MockLedger : public bcos::ledger::LedgerInterface } _callback(nullptr); } - void asyncGetStorageAt(std::string_view, std::string_view, protocol::BlockNumber, - std::function _onGetStorage) override - { - _onGetStorage(nullptr, ""); - } bcos::Error::Ptr storeTransactionsAndReceipts( bcos::protocol::ConstTransactionsPtr, bcos::protocol::Block::ConstPtr) override @@ -140,6 +135,11 @@ class MockLedger : public bcos::ledger::LedgerInterface _onGetConfig(nullptr, std::to_string(MockLedger::TX_GAS_LIMIT), m_blockNumber); return; } + else if (std::string(bcos::ledger::SYSTEM_KEY_WEB3_CHAIN_ID) == std::string(_key)) + { + _onGetConfig(nullptr, "20200", m_blockNumber); + return; + } else if (std::string(bcos::ledger::SYSTEM_KEY_AUTH_CHECK_STATUS) == std::string(_key)) { if (m_storage) diff --git a/bcos-framework/bcos-framework/ledger/GenesisConfig.h b/bcos-framework/bcos-framework/ledger/GenesisConfig.h index 9f1605f6c4..915af07592 100644 --- a/bcos-framework/bcos-framework/ledger/GenesisConfig.h +++ b/bcos-framework/bcos-framework/ledger/GenesisConfig.h @@ -59,6 +59,9 @@ class GenesisConfig std::string m_chainID; std::string m_groupID; + // web3 chain config + std::string m_web3ChainID; + // consensus config std::string m_consensusType; uint64_t m_txCountLimit = 1000; diff --git a/bcos-framework/bcos-framework/ledger/Ledger.h b/bcos-framework/bcos-framework/ledger/Ledger.h index 62724811b9..771d69e471 100644 --- a/bcos-framework/bcos-framework/ledger/Ledger.h +++ b/bcos-framework/bcos-framework/ledger/Ledger.h @@ -143,8 +143,8 @@ inline constexpr struct GetTransactions inline constexpr struct GetStorageAt { - task::Task operator()(auto& ledger, std::string_view address, std::string_view key, - bcos::protocol::BlockNumber number) const + task::Task> operator()(auto& ledger, + std::string_view address, std::string_view key, bcos::protocol::BlockNumber number) const { co_return co_await tag_invoke(*this, ledger, address, key, number); } diff --git a/bcos-framework/bcos-framework/ledger/LedgerConfig.h b/bcos-framework/bcos-framework/ledger/LedgerConfig.h index 6e485020db..9404233bb1 100644 --- a/bcos-framework/bcos-framework/ledger/LedgerConfig.h +++ b/bcos-framework/bcos-framework/ledger/LedgerConfig.h @@ -23,6 +23,8 @@ #include "../protocol/ProtocolTypeDef.h" #include "Features.h" +#include + namespace bcos::ledger { @@ -156,6 +158,9 @@ class LedgerConfig Features const& features() const { return m_features; } void setFeatures(Features features) { m_features = features; } + std::optional const& chainId() const { return m_chainId; } + void setChainId(evmc_uint256be _chainId) { m_chainId = std::move(_chainId); } + private: bcos::consensus::ConsensusNodeListPtr m_consensusNodeList; bcos::consensus::ConsensusNodeListPtr m_observerNodeList; @@ -178,5 +183,6 @@ class LedgerConfig int64_t m_txsSize = -1; uint32_t m_authCheckStatus = 0; Features m_features; + std::optional m_chainId = {}; }; } // namespace bcos::ledger diff --git a/bcos-framework/bcos-framework/ledger/LedgerInterface.h b/bcos-framework/bcos-framework/ledger/LedgerInterface.h index e2525b1c3b..d201b72db4 100644 --- a/bcos-framework/bcos-framework/ledger/LedgerInterface.h +++ b/bcos-framework/bcos-framework/ledger/LedgerInterface.h @@ -170,18 +170,20 @@ class LedgerInterface std::function _callback) = 0; /** - * @brief Async get storage value by address and key and block number. It will access the table - * of address like /apps/[address] and get the value of key. NOTE: blockNumber is ignored + * @brief get storage value by address and key and block number in coroutine. It will access the + * table of address like /apps/[address] and get the value of key. NOTE: blockNumber is ignored * nowadays, it will always get the latest value of key in address. * @param _address the address of contract/EOA. if in EVM, it should be the address of contract, * hex string; if in WASM, it should be the path name of contract. * @param _key the key of storage * @param _blockNumber the block number to get the storage value - * @param _onGetStorage callback when get storage value, + * @return the storage value of key in address */ - virtual void asyncGetStorageAt(std::string_view _address, std::string_view _key, - protocol::BlockNumber _blockNumber, - std::function _onGetStorage) = 0; + virtual task::Task> getStorageAt( + std::string_view _address, std::string_view _key, protocol::BlockNumber _blockNumber) + { + co_return std::nullopt; + } }; } // namespace bcos::ledger diff --git a/bcos-framework/bcos-framework/ledger/LedgerTypeDef.h b/bcos-framework/bcos-framework/ledger/LedgerTypeDef.h index 4f602c5a14..b36a9b6458 100644 --- a/bcos-framework/bcos-framework/ledger/LedgerTypeDef.h +++ b/bcos-framework/bcos-framework/ledger/LedgerTypeDef.h @@ -35,6 +35,7 @@ constexpr static int32_t TRANSACTIONS = 0x0004; constexpr static int32_t RECEIPTS = 0x0002; // get system config key +constexpr static std::string_view SYSTEM_KEY_WEB3_CHAIN_ID = "web3_chain_id"; constexpr static std::string_view SYSTEM_KEY_TX_GAS_LIMIT = "tx_gas_limit"; constexpr static std::string_view SYSTEM_KEY_TX_GAS_PRICE = "tx_gas_price"; constexpr static std::string_view SYSTEM_KEY_TX_COUNT_LIMIT = "tx_count_limit"; diff --git a/bcos-framework/bcos-framework/protocol/Protocol.h b/bcos-framework/bcos-framework/protocol/Protocol.h index cd0ceaa444..d00b205e12 100644 --- a/bcos-framework/bcos-framework/protocol/Protocol.h +++ b/bcos-framework/bcos-framework/protocol/Protocol.h @@ -115,6 +115,7 @@ enum ProtocolVersion : uint32_t enum class BlockVersion : uint32_t { + V3_9_0_VERSION = 0x03090000, V3_8_0_VERSION = 0x03080000, V3_7_2_VERSION = 0x03070200, V3_7_1_VERSION = 0x03070100, @@ -135,7 +136,7 @@ enum class BlockVersion : uint32_t V3_0_VERSION = 0x03000000, RC4_VERSION = 4, MIN_VERSION = RC4_VERSION, - MAX_VERSION = V3_8_0_VERSION, + MAX_VERSION = V3_9_0_VERSION, }; enum class TransactionVersion : uint32_t @@ -148,10 +149,10 @@ enum class TransactionVersion : uint32_t const std::string RC4_VERSION_STR = "3.0.0-rc4"; const std::string RC_VERSION_PREFIX = "3.0.0-rc"; -const std::string V3_8_VERSION_STR = "3.8.0"; +const std::string V3_9_VERSION_STR = "3.9.0"; -const BlockVersion DEFAULT_VERSION = bcos::protocol::BlockVersion::V3_8_0_VERSION; -const std::string DEFAULT_VERSION_STR = V3_8_VERSION_STR; +const BlockVersion DEFAULT_VERSION = bcos::protocol::BlockVersion::V3_9_0_VERSION; +const std::string DEFAULT_VERSION_STR = V3_9_VERSION_STR; const uint8_t MAX_MAJOR_VERSION = std::numeric_limits::max(); const uint8_t MIN_MAJOR_VERSION = 3; diff --git a/bcos-framework/bcos-framework/protocol/Transaction.h b/bcos-framework/bcos-framework/protocol/Transaction.h index 27ba0adcce..66f7cb88bd 100644 --- a/bcos-framework/bcos-framework/protocol/Transaction.h +++ b/bcos-framework/bcos-framework/protocol/Transaction.h @@ -42,6 +42,20 @@ enum class TransactionType : uint8_t Web3Transacion = 1, }; +constexpr auto operator<=>(bcos::protocol::TransactionType const& _lhs, auto _rhs) + requires(std::same_as || + std::unsigned_integral) +{ + return static_cast(_lhs) <=> static_cast(_rhs); +} + +constexpr bool operator==(bcos::protocol::TransactionType const& _lhs, auto _rhs) + requires(std::same_as || + std::unsigned_integral) +{ + return static_cast(_lhs) == static_cast(_rhs); +} + enum TransactionOp { NullTransaction = 0, diff --git a/bcos-framework/bcos-framework/testutils/faker/FakeLedger.h b/bcos-framework/bcos-framework/testutils/faker/FakeLedger.h index 4384f297e7..21f78dd54b 100644 --- a/bcos-framework/bcos-framework/testutils/faker/FakeLedger.h +++ b/bcos-framework/bcos-framework/testutils/faker/FakeLedger.h @@ -162,12 +162,6 @@ class FakeLedger : public LedgerInterface, public std::enable_shared_from_this _onGetStorage) override - { - _onGetStorage(nullptr, ""); - } - // the txpool module use this interface to store txs bcos::Error::Ptr storeTransactionsAndReceipts(bcos::protocol::ConstTransactionsPtr blockTxs, bcos::protocol::Block::ConstPtr block) override @@ -264,7 +258,7 @@ class FakeLedger : public LedgerInterface, public std::enable_shared_from_thisblockNumber()); + _onGetConfig(BCOS_ERROR_PTR(3008, "key not found"), "", m_ledgerConfig->blockNumber()); return; } _onGetConfig(nullptr, value, m_ledgerConfig->blockNumber()); diff --git a/bcos-ledger/src/libledger/Ledger.cpp b/bcos-ledger/src/libledger/Ledger.cpp index 423b6d2ff7..83071c527b 100644 --- a/bcos-ledger/src/libledger/Ledger.cpp +++ b/bcos-ledger/src/libledger/Ledger.cpp @@ -124,48 +124,17 @@ void Ledger::asyncPreStoreBlockTxs(bcos::protocol::ConstTransactionsPtr _blockTx _callback(nullptr); } -void Ledger::asyncGetStorageAt(std::string_view _address, std::string_view _key, - protocol::BlockNumber _blockNumber, std::function _onGetStorage) +task::Task> Ledger::getStorageAt( + std::string_view _address, std::string_view _key, protocol::BlockNumber _blockNumber) { - auto contractTableName = getContractTableName(SYS_DIRECTORY::USER_APPS, _address); - // TODO: blockNumber is not used nowadays + // TODO)): blockNumber is not used nowadays std::ignore = _blockNumber; - LEDGER_LOG(TRACE) << LOG_DESC("asyncGetStorageAt") << LOG_KV("address", _address) - << LOG_KV("key", _key) << LOG_KV("blockNumber", _blockNumber); - m_storage->asyncGetRow(contractTableName, _key, - [onGetStorage = std::move(_onGetStorage)](auto&& _error, auto&& _entry) { - if (_error) - { - onGetStorage(std::move(_error), ""); - return; - } - if (!_entry) - { - onGetStorage( - BCOS_ERROR_PTR(GetStorageError, "Cannot get storage in address and key"), ""); - return; - } - auto value = _entry->exportFields(); - std::visit( - [onGetStorage = std::move(onGetStorage)](auto&& valueInside) { - std::string realValue{}; - if constexpr (concepts::PointerLike) - { - realValue = std::string( - reinterpret_cast(valueInside->data()), valueInside->size()); - } - else - { - realValue = std::string( - reinterpret_cast(valueInside.data()), valueInside.size()); - } - onGetStorage(nullptr, std::move(realValue)); - }, - value); - }); + auto const contractTableName = getContractTableName(SYS_DIRECTORY::USER_APPS, _address); + auto const stateStorage = getStateStorage(); + co_return co_await bcos::storage2::readOne( + *stateStorage, transaction_executor::StateKeyView{contractTableName, _key}); } - void Ledger::asyncPrewriteBlock(bcos::storage::StorageInterface::Ptr storage, bcos::protocol::ConstTransactionsPtr _blockTxs, bcos::protocol::Block::ConstPtr block, std::function callback, @@ -2011,6 +1980,14 @@ bool Ledger::buildGenesisBlock( sysTable->setRow(SYSTEM_KEY_AUTH_CHECK_STATUS, std::move(authCheckStatusEntry)); } + if (versionNumber >= BlockVersion::V3_9_0_VERSION) + { + // write web3 chain id + Entry chainIdEntry; + chainIdEntry.setObject(SystemConfigEntry{genesis.m_web3ChainID, 0}); + sysTable->setRow(SYSTEM_KEY_WEB3_CHAIN_ID, std::move(chainIdEntry)); + } + // write consensus node list std::promise>> consensusTablePromise; m_storage->asyncOpenTable(SYS_CONSENSUS, [&consensusTablePromise]( diff --git a/bcos-ledger/src/libledger/Ledger.h b/bcos-ledger/src/libledger/Ledger.h index cb995bee27..bea792f4e8 100644 --- a/bcos-ledger/src/libledger/Ledger.h +++ b/bcos-ledger/src/libledger/Ledger.h @@ -29,6 +29,7 @@ #include "bcos-framework/storage/Common.h" #include "bcos-framework/storage/StorageInterface.h" #include "utilities/Common.h" +#include #include #include #include @@ -111,14 +112,40 @@ class Ledger : public LedgerInterface std::function&&)> _callback) override; - void asyncGetStorageAt(std::string_view _address, std::string_view _key, - protocol::BlockNumber _blockNumber, - std::function _onGetStorage) override; + task::Task> getStorageAt(std::string_view _address, + std::string_view _key, protocol::BlockNumber _blockNumber) override; bool buildGenesisBlock(GenesisConfig const& genesis, ledger::LedgerConfig const& ledgerConfig); void asyncGetBlockTransactionHashes(bcos::protocol::BlockNumber blockNumber, std::function&&)> callback); + void setKeyPageSize(size_t keyPageSize) { m_keyPageSize = keyPageSize; } + +protected: + storage::StateStorageInterface::Ptr getStateStorage() + { + if (m_keyPageSize > 0) + { + // create keyPageStorage + storage::StateStorageFactory stateStorageFactory(m_keyPageSize); + // getABI function begin in version 320 + auto keyPageIgnoreTables = std::make_shared>>( + storage::IGNORED_ARRAY_310.begin(), storage::IGNORED_ARRAY_310.end()); + auto [error, entry] = + m_storage->getRow(ledger::SYS_CONFIG, ledger::SYSTEM_KEY_COMPATIBILITY_VERSION); + if (!entry || error) + { + BOOST_THROW_EXCEPTION( + BCOS_ERROR(GetStorageError, "Not found compatibilityVersion.")); + } + auto [compatibilityVersionStr, _] = entry->template getObject(); + auto const version = bcos::tool::toVersionNumber(compatibilityVersionStr); + auto stateStorage = stateStorageFactory.createStateStorage( + m_storage, version, true, std::move(keyPageIgnoreTables)); + return stateStorage; + } + return std::make_shared(m_storage); + } private: Error::Ptr checkTableValid(Error::UniquePtr&& error, @@ -176,5 +203,6 @@ class Ledger : public LedgerInterface RecursiveMutex m_receiptMerkleMtx; CacheType m_txProofMerkleCache; CacheType m_receiptProofMerkleCache; + size_t m_keyPageSize = 0; }; } // namespace bcos::ledger diff --git a/bcos-ledger/src/libledger/LedgerMethods.cpp b/bcos-ledger/src/libledger/LedgerMethods.cpp index d7dea16b03..47f5f00fa4 100644 --- a/bcos-ledger/src/libledger/LedgerMethods.cpp +++ b/bcos-ledger/src/libledger/LedgerMethods.cpp @@ -438,7 +438,8 @@ bcos::task::Task bcos::ledger::tag_invoke( } ledgerConfig->setAuthCheckStatus( std::get<0>(co_await getSystemConfigOrDefault(ledger, SYSTEM_KEY_AUTH_CHECK_STATUS, 0))); - + auto [chainId, _] = co_await getSystemConfigOrDefault(ledger, SYSTEM_KEY_WEB3_CHAIN_ID, "0"); + ledgerConfig->setChainId(bcos::toEvmC(boost::lexical_cast(chainId))); co_return ledgerConfig; } @@ -553,46 +554,9 @@ bcos::task::Task bcos::ledger::tag_invoke( co_return co_await awaitable; } -bcos::task::Task bcos::ledger::tag_invoke(ledger::tag_t, - LedgerInterface& ledger, std::string_view address, std::string_view key, - bcos::protocol::BlockNumber number) +bcos::task::Task> bcos::ledger::tag_invoke( + ledger::tag_t, LedgerInterface& ledger, std::string_view address, + std::string_view key, bcos::protocol::BlockNumber number) { - struct Awaitable - { - bcos::ledger::LedgerInterface& m_ledger; - std::string_view m_address; - std::string_view m_key; - bcos::protocol::BlockNumber m_number; - - std::variant m_result; - - constexpr static bool await_ready() noexcept { return false; } - void await_suspend(CO_STD::coroutine_handle<> handle) - { - m_ledger.asyncGetStorageAt( - m_address, m_key, m_number, [this, handle](auto&& error, auto&& value) { - if (error) - { - m_result.emplace(std::move(error)); - } - else - { - m_result.emplace(value); - } - handle.resume(); - }); - } - std::string await_resume() - { - if (std::holds_alternative(m_result)) - { - BOOST_THROW_EXCEPTION(*std::get(m_result)); - } - return std::get(m_result); - } - }; - - Awaitable awaitable{ - .m_ledger = ledger, .m_address = address, .m_key = key, .m_number = number, .m_result = {}}; - co_return co_await awaitable; + co_return co_await ledger.getStorageAt(address, key, number); } diff --git a/bcos-ledger/src/libledger/LedgerMethods.h b/bcos-ledger/src/libledger/LedgerMethods.h index 34ac88312a..a875232625 100644 --- a/bcos-ledger/src/libledger/LedgerMethods.h +++ b/bcos-ledger/src/libledger/LedgerMethods.h @@ -95,7 +95,8 @@ task::Task tag_invoke( task::Task tag_invoke( ledger::tag_t, LedgerInterface& ledger, crypto::HashListPtr hashes); -task::Task tag_invoke(ledger::tag_t, LedgerInterface& ledger, - std::string_view address, std::string_view key, bcos::protocol::BlockNumber number); +task::Task> tag_invoke(ledger::tag_t, + LedgerInterface& ledger, std::string_view address, std::string_view key, + bcos::protocol::BlockNumber number); } // namespace bcos::ledger \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp index cc612cf3e4..87414b2af6 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp @@ -59,7 +59,25 @@ task::Task EthEndpoint::coinbase(const Json::Value&, Json::Value&) } task::Task EthEndpoint::chainId(const Json::Value&, Json::Value& response) { - Json::Value result = "0x4ee8"; // 20200 + auto const ledger = m_nodeService->ledger(); + auto config = co_await ledger::getSystemConfig(*ledger, ledger::SYSTEM_KEY_WEB3_CHAIN_ID); + Json::Value result; + if (config.has_value()) + { + try + { + auto [chainId, _] = config.value(); + result = toQuantity(std::stoull(chainId)); + } + catch (...) + { + result = "0x4ee8"; // 20200 + } + } + else + { + result = "0x4ee8"; // 20200 + } buildJsonContent(result, response); co_return; } @@ -78,15 +96,14 @@ task::Task EthEndpoint::hashrate(const Json::Value&, Json::Value& response task::Task EthEndpoint::gasPrice(const Json::Value&, Json::Value& response) { // result: gasPrice(QTY) - auto ledger = m_nodeService->ledger(); - // TODO: to check gas price is hex value or not + auto const ledger = m_nodeService->ledger(); auto config = co_await ledger::getSystemConfig(*ledger, ledger::SYSTEM_KEY_TX_GAS_PRICE); Json::Value result; if (config.has_value()) { auto [gasPrice, _] = config.value(); auto const value = std::stoull(gasPrice); - result = toQuantity(value); + result = toQuantity(value < LowestGasPrice ? LowestGasPrice : value); } else { @@ -113,25 +130,31 @@ task::Task EthEndpoint::getBalance(const Json::Value& request, Json::Value { // params: address(DATA), blockNumber(QTY|TAG) // result: balance(QTY) - auto const address = toView(request[0u]); + auto address = toView(request[0u]); + if (address.starts_with("0x") || address.starts_with("0X")) + { + address.remove_prefix(2); + } + std::string addressStr(address); + boost::algorithm::to_lower(addressStr); // TODO)): blockNumber is ignored nowadays // auto const blockTag = toView(request[1u]); // auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); auto const ledger = m_nodeService->ledger(); - u256 balance; - try + u256 balance = 0; + if (auto const entry = co_await ledger::getStorageAt( + *ledger, addressStr, bcos::executor::ACCOUNT_BALANCE, /*blockNumber*/ 0); + entry.has_value()) { - auto const value = co_await ledger::getStorageAt( - *ledger, address, bcos::executor::ACCOUNT_BALANCE, /*blockNumber*/ 0); - balance = u256(value); + auto const balanceStr = std::string(entry.value().get()); + balance = u256(balanceStr); } - catch (...) + else { WEB3_LOG(TRACE) << LOG_DESC("getBalance failed, return 0 by defualt") << LOG_KV("address", address); - balance = 0; } - Json::Value result = toQuantity(balance); + Json::Value result = toQuantity(std::move(balance)); buildJsonContent(result, response); co_return; } @@ -139,14 +162,29 @@ task::Task EthEndpoint::getStorageAt(const Json::Value& request, Json::Val { // params: address(DATA), position(QTY), blockNumber(QTY|TAG) // result: value(DATA) - auto const address = toView(request[0u]); + auto address = toView(request[0u]); + if (address.starts_with("0x") || address.starts_with("0X")) + { + address.remove_prefix(2); + } + std::string addressStr(address); + boost::algorithm::to_lower(addressStr); auto const position = toView(request[1u]); // TODO)): blockNumber is ignored nowadays // auto const blockTag = toView(request[2u]); // auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); auto const ledger = m_nodeService->ledger(); - auto const value = co_await ledger::getStorageAt(*ledger, address, position, /*blockNumber*/ 0); - Json::Value result = value; + Json::Value result; + if (auto const entry = + co_await ledger::getStorageAt(*ledger, addressStr, position, /*blockNumber*/ 0); + entry.has_value()) + { + result = std::string(entry.value().get()); + } + else + { + BOOST_THROW_EXCEPTION(JsonRpcException(JsonRpcError::InvalidParams, "Invalid params!")); + } buildJsonContent(result, response); co_return; } @@ -154,19 +192,25 @@ task::Task EthEndpoint::getTransactionCount(const Json::Value& request, Js { // params: address(DATA), blockNumber(QTY|TAG) // result: nonce(QTY) - auto const address = toView(request[0u]); + auto address = toView(request[0u]); + if (address.starts_with("0x") || address.starts_with("0X")) + { + address.remove_prefix(2); + } + std::string addressStr(address); + boost::algorithm::to_lower(addressStr); // TODO)): blockNumber is ignored nowadays // auto const blockTag = toView(request[1u]); // auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); auto const ledger = m_nodeService->ledger(); uint64_t nonce = 0; - try + if (auto const entry = co_await ledger::getStorageAt( + *ledger, addressStr, bcos::ledger::ACCOUNT_TABLE_FIELDS::NONCE, /*blockNumber*/ 0); + entry.has_value()) { - auto const value = co_await ledger::getStorageAt( - *ledger, address, bcos::executor::ACCOUNT_NONCE, /*blockNumber*/ 0); - nonce = std::stoull(value); + nonce = std::stoull(std::string(entry.value().get())); } - catch (...) + else { WEB3_LOG(TRACE) << LOG_DESC("get address nonce failed, return random value by defualt") << LOG_KV("address", address); @@ -210,15 +254,13 @@ task::Task EthEndpoint::getBlockTxCountByNumber( buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::getUncleCountByBlockHash( - const Json::Value& request, Json::Value& response) +task::Task EthEndpoint::getUncleCountByBlockHash(const Json::Value&, Json::Value& response) { Json::Value result = "0x0"; buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::getUncleCountByBlockNumber( - const Json::Value& request, Json::Value& response) +task::Task EthEndpoint::getUncleCountByBlockNumber(const Json::Value&, Json::Value& response) { Json::Value result = "0x0"; buildJsonContent(result, response); @@ -228,7 +270,13 @@ task::Task EthEndpoint::getCode(const Json::Value& request, Json::Value& r { // params: address(DATA), blockNumber(QTY|TAG) // result: code(DATA) - auto const address = toView(request[0u]); + auto address = toView(request[0u]); + if (address.starts_with("0x") || address.starts_with("0X")) + { + address.remove_prefix(2); + } + std::string addressStr(address); + boost::algorithm::to_lower(addressStr); // TODO)): blockNumber is ignored nowadays // auto const blockTag = toView(request[1u]); // auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); @@ -236,7 +284,7 @@ task::Task EthEndpoint::getCode(const Json::Value& request, Json::Value& r struct Awaitable { bcos::scheduler::SchedulerInterface::Ptr m_scheduler; - std::string_view m_address; + std::string m_address; std::variant m_result{}; constexpr static bool await_ready() noexcept { return false; } void await_suspend(CO_STD::coroutine_handle<> handle) noexcept @@ -264,7 +312,7 @@ task::Task EthEndpoint::getCode(const Json::Value& request, Json::Value& r }; auto const code = co_await Awaitable{ .m_scheduler = scheduler, - .m_address = address, + .m_address = std::move(addressStr), }; Json::Value result = toHexStringWithPrefix(code); buildJsonContent(result, response); @@ -312,13 +360,13 @@ task::Task EthEndpoint::sendRawTransaction(const Json::Value& request, Jso { BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParams, error->errorMessage())); } - auto tarsTx = web3Tx.toTarsTransaction(); + auto tarsTx = web3Tx.takeToTarsTransaction(); auto tx = std::make_shared( [m_tx = std::move(tarsTx)]() mutable { return &m_tx; }); // for web3.eth.sendRawTransaction, return the hash of raw transaction auto web3TxHash = bcos::crypto::keccak256Hash(bcos::ref(rawTxBytes)); - tx->mutableInner().dataHash.assign(web3TxHash.begin(), web3TxHash.end()); + tx->mutableInner().extraTransactionHash.assign(web3TxHash.begin(), web3TxHash.end()); if (c_fileLogLevel == TRACE) { diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.cpp deleted file mode 100644 index d51a6076a4..0000000000 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (C) 2024 FISCO BCOS. - * SPDX-License-Identifier: Apache-2.0 - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @file BlockResponse.cpp - * @author: kyonGuo - * @date 2024/4/11 - */ - -#include "BlockResponse.h" diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h index 8f123dabb8..9592fe8d3b 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h @@ -34,77 +34,6 @@ namespace bcos::rpc { -// maybe this struct is unnecessary... -struct ReceiptResponse -{ - ReceiptResponse() = default; - explicit ReceiptResponse(bcos::protocol::TransactionReceipt::ConstPtr&& receipt) - { - status = (receipt->status() == 0 ? 1 : 0); - txHash = receipt->hash(); - effectiveGasPrice = std::string(receipt->effectiveGasPrice()); - gasUsed = receipt->gasUsed(); - blockNumber = receipt->blockNumber(); - if (!receipt->contractAddress().empty()) - { - contractAddress = Address(std::string(receipt->contractAddress())); - } - output = receipt->output().toBytes(); - auto mutableReceipt = const_cast(receipt.get()); - auto receiptLog = mutableReceipt->takeLogEntris(); - this->logs.reserve(receiptLog.size()); - for (size_t i = 0; i < receiptLog.size(); i++) - { - rpc::Log log{.address = std::move(receiptLog[i].takeAddress()), - .topics = std::move(receiptLog[i].takeTopics()), - .data = std::move(receiptLog[i].takeData())}; - log.logIndex = i; - log.number = blockNumber; - this->logs.push_back(std::move(log)); - } - logsBloom = getLogsBloom(logs); - } - int32_t status{0}; - uint32_t txIndex{0}; - crypto::HashType txHash{}; - crypto::HashType blockHash{}; - // Quantity - protocol::BlockNumber blockNumber{0}; - std::string from{}; - std::string to{}; - u256 cumulativeGasUsed{0}; - std::string effectiveGasPrice{}; - u256 gasUsed{0}; - // only has value in create tx - std::optional
contractAddress{}; - TransactionType type = TransactionType::Legacy; - Logs logs{}; - - Bloom logsBloom{}; - bcos::bytes output{}; - - void updateTxInfo(bcos::protocol::Transaction const& tx) - { - // txHash = tx.hash(); - // from = std::string(tx.sender()); - // to = tx.to().empty() ? "" : std::string(tx.to()); - // if (!tx.extraTransactionBytes().empty()) - // { - // if (auto firstByte = tx.extraTransactionBytes()[0]; - // firstByte < bcos::codec::rlp::BYTES_HEAD_BASE) - // { - // type = static_cast(firstByte); - // } - // } - } - - // TODO: update block info - void updateBlockInfo(bcos::protocol::Block const& block) {} - - // TODO: to json - void toJson(Json::Value&) {} -}; - [[maybe_unused]] static void combineReceiptResponse(Json::Value& result, protocol::TransactionReceipt::ConstPtr&& receipt, bcos::protocol::Transaction::ConstPtr&& tx, bcos::protocol::Block::Ptr&& block) diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h index fb411ad18e..8e5c345fda 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h @@ -71,21 +71,36 @@ static void combineTxResponse(Json::Value& result, bcos::protocol::Transaction:: result["gasPrice"] = std::string(receipt ? receipt->effectiveGasPrice() : tx->gasPrice()); result["hash"] = tx->hash().hexPrefixed(); result["input"] = toHexStringWithPrefix(tx->input()); - Web3Transaction web3Tx; - auto extraBytesRef = bcos::bytesRef( - const_cast(tx->extraTransactionBytes().data()), tx->extraTransactionBytes().size()); - codec::rlp::decode(extraBytesRef, web3Tx); - result["nonce"] = toQuantity(web3Tx.nonce); - result["type"] = toQuantity(static_cast(web3Tx.type)); - result["value"] = toQuantity(web3Tx.value); + + if (tx->type() == bcos::protocol::TransactionType::BCOSTransaction) [[unlikely]] + { + result["type"] = toQuantity(UINT32_MAX); + result["nonce"] = tx->nonce(); + result["value"] = std::string(tx->value().empty() ? "0x0" : tx->value()); + result["maxPriorityFeePerGas"] = + std::string(tx->maxPriorityFeePerGas().empty() ? "0x0" : tx->maxPriorityFeePerGas()); + result["maxFeePerGas"] = + std::string(tx->maxFeePerGas().empty() ? "0x0" : tx->maxFeePerGas()); + result["chainId"] = "0x0"; + } + else [[likely]] + { + Web3Transaction web3Tx; + auto extraBytesRef = bcos::bytesRef(const_cast(tx->extraTransactionBytes().data()), + tx->extraTransactionBytes().size()); + codec::rlp::decode(extraBytesRef, web3Tx); + result["nonce"] = toQuantity(web3Tx.nonce); + result["type"] = toQuantity(static_cast(web3Tx.type)); + result["value"] = toQuantity(web3Tx.value); + if (web3Tx.type >= TransactionType::EIP1559) + { + result["maxPriorityFeePerGas"] = toQuantity(web3Tx.maxPriorityFeePerGas); + result["maxFeePerGas"] = toQuantity(web3Tx.maxFeePerGas); + } + result["chainId"] = toQuantity(web3Tx.chainId.value_or(0)); + } result["r"] = toQuantity(tx->signatureData().getCroppedData(0, 32)); result["s"] = toQuantity(tx->signatureData().getCroppedData(32, 32)); result["v"] = toQuantity(tx->signatureData().getCroppedData(64, 1)); - if (web3Tx.type >= TransactionType::EIP1559) - { - result["maxPriorityFeePerGas"] = toQuantity(web3Tx.maxPriorityFeePerGas); - result["maxFeePerGas"] = toQuantity(web3Tx.maxFeePerGas); - } - result["chainId"] = toQuantity(web3Tx.chainId.value_or(0)); } } // namespace bcos::rpc diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp index 33ebaae179..f41042ef24 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp @@ -95,19 +95,25 @@ bcos::bytes Web3Transaction::encode() const return out; } -bcos::crypto::HashType Web3Transaction::hash() const +bcos::crypto::HashType Web3Transaction::txHash() const { bcos::bytes encoded{}; codec::rlp::encode(encoded, *this); return bcos::crypto::keccak256Hash(bcos::ref(encoded)); } -bcostars::Transaction Web3Transaction::toTarsTransaction() const +bcos::crypto::HashType Web3Transaction::hashForSign() const +{ + auto encodeForSign = this->encode(); + return bcos::crypto::keccak256Hash(bcos::ref(encodeForSign)); +} + +bcostars::Transaction Web3Transaction::takeToTarsTransaction() { bcostars::Transaction tarsTx{}; - tarsTx.data.nonce = toHex(this->nonce); tarsTx.data.to = (this->to == Address()) ? "" : this->to.hexPrefixed(); - tarsTx.data.input.insert(tarsTx.data.input.end(), this->data.begin(), this->data.end()); + tarsTx.data.input.reserve(this->data.size()); + RANGES::move(this->data, std::back_inserter(tarsTx.data.input)); tarsTx.data.value = toHex(this->value); tarsTx.data.gasLimit = this->gasLimit; if (static_cast(this->type) >= static_cast(TransactionType::EIP1559)) @@ -119,21 +125,28 @@ bcostars::Transaction Web3Transaction::toTarsTransaction() const { tarsTx.data.gasPrice = toHex(this->maxPriorityFeePerGas); } - auto hash = this->hash(); + tarsTx.type = static_cast(bcos::protocol::TransactionType::Web3Transacion); + auto hashForSign = this->hashForSign(); auto encodedForSign = this->encode(); - tarsTx.dataHash.insert(tarsTx.dataHash.end(), hash.begin(), hash.end()); // FISCO BCOS signature is r||s||v - tarsTx.signature.insert( - tarsTx.signature.end(), this->signatureR.begin(), this->signatureR.end()); - tarsTx.signature.insert( - tarsTx.signature.end(), this->signatureS.begin(), this->signatureS.end()); + tarsTx.signature.reserve(crypto::SECP256K1_SIGNATURE_LEN); + RANGES::move(this->signatureR, std::back_inserter(tarsTx.signature)); + RANGES::move(this->signatureS, std::back_inserter(tarsTx.signature)); tarsTx.signature.push_back(static_cast(this->signatureV)); - tarsTx.type = static_cast(bcos::protocol::TransactionType::Web3Transacion); - - tarsTx.extraTransactionBytes.insert( - tarsTx.extraTransactionBytes.end(), encodedForSign.begin(), encodedForSign.end()); + tarsTx.extraTransactionBytes.reserve(encodedForSign.size()); + RANGES::move(encodedForSign, std::back_inserter(tarsTx.extraTransactionBytes)); + const bcos::crypto::Secp256k1Crypto signatureImpl; + bcos::crypto::Keccak256 hashImpl; + auto signRef = bcos::bytesConstRef( + reinterpret_cast(tarsTx.signature.data()), tarsTx.signature.size()); + auto [_, sender] = signatureImpl.recoverAddress(hashImpl, hashForSign, signRef); + tarsTx.data.nonce = toHexStringWithPrefix(sender) + toQuantity(this->nonce); + tarsTx.dataHash.reserve(crypto::HashType::SIZE); + RANGES::move(hashForSign, std::back_inserter(tarsTx.dataHash)); + tarsTx.sender.reserve(sender.size()); + RANGES::move(sender, std::back_inserter(tarsTx.sender)); return tarsTx; } } // namespace rpc diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h index 5157c91f31..56ab243707 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h @@ -77,8 +77,11 @@ class Web3Transaction Web3Transaction& operator=(Web3Transaction&&) = default; bcos::bytes encode() const; - bcos::crypto::HashType hash() const; - bcostars::Transaction toTarsTransaction() const; + // tx hash = keccak256(rlp(tx_payload,v,r,s)) + bcos::crypto::HashType txHash() const; + // hash for sign = keccak256(rlp(tx_payload)) + bcos::crypto::HashType hashForSign() const; + bcostars::Transaction takeToTarsTransaction(); uint64_t getSignatureV() const { // EIP-155: Simple replay attack protection @@ -136,6 +139,7 @@ class Web3Transaction // EIP-4844: Shard Blob Transactions uint64_t maxFeePerBlobGas{0}; h256s blobVersionedHashes{}; + // TODO)) blob bcos::bytes signatureR{}; bcos::bytes signatureS{}; uint64_t signatureV{0}; diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/utils/Common.h b/bcos-rpc/bcos-rpc/web3jsonrpc/utils/Common.h index 09a32f6281..6d0d089cd4 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/utils/Common.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/utils/Common.h @@ -25,6 +25,7 @@ namespace bcos::rpc { +constexpr const uint64_t LowestGasPrice{21000}; constexpr const std::string_view EarliestBlock{"earliest"}; constexpr const std::string_view LatestBlock{"latest"}; constexpr const std::string_view PendingBlock{"pending"}; diff --git a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp index 1f6926c634..0120cedd09 100644 --- a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp +++ b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp @@ -181,7 +181,7 @@ BOOST_AUTO_TEST_CASE(handleValidTest) // method eth_gasPrice { - m_ledger->setSystemConfig(SYSTEM_KEY_TX_GAS_PRICE, "10086"); + m_ledger->setSystemConfig(SYSTEM_KEY_TX_GAS_PRICE, "10086000"); const auto request = R"({"jsonrpc":"2.0","id":541321, "method":"eth_gasPrice","params":[]})"; auto response = onRPCRequestWrapper(request); @@ -189,8 +189,8 @@ BOOST_AUTO_TEST_CASE(handleValidTest) BOOST_CHECK(response["id"].asInt64() == 541321); auto b = fromQuantity(response["result"].asString()); std::cout << b << std::endl; - BOOST_CHECK(b == 10086); - BOOST_CHECK(fromQuantity(response["result"].asString()) == 10086); + BOOST_CHECK(b == 10086000); + BOOST_CHECK(fromQuantity(response["result"].asString()) == 10086000); } // method eth_accounts diff --git a/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp b/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp index 6b92e36407..b0668c85dd 100644 --- a/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp +++ b/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp @@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(testLegacyTransactionDecode) toHex(tx.signatureS), "2d690516512020171c1ec870f6ff45398cc8609250326be89915fb538e7bd718"); BOOST_CHECK_EQUAL(tx.getSignatureV(), tx.chainId.value() * 2 + 35 + 1); - auto hash = tx.hash().hexPrefixed(); + auto hash = tx.txHash().hexPrefixed(); BOOST_CHECK_EQUAL(hash, bcos::crypto::keccak256Hash(ref(bytes)).hexPrefixed()); bcos::bytes encoded{}; codec::rlp::encode(encoded, tx); @@ -234,7 +234,7 @@ BOOST_AUTO_TEST_CASE(EIP1559Recover) BOOST_CHECK_EQUAL(tx.value, 498134000000000000ull); BOOST_CHECK_EQUAL(toHex(tx.data), ""); BOOST_CHECK_EQUAL(tx.getSignatureV(), tx.chainId.value() * 2 + 35); - BOOST_CHECK_EQUAL(tx.hash().hexPrefixed(), + BOOST_CHECK_EQUAL(tx.txHash().hexPrefixed(), "0xcf6b53ec88659fc86e854af2e8453fa519ca261f949ef291e33c5f44ead870dc"); auto txHash = bcos::crypto::keccak256Hash(ref(bytes)).hexPrefixed(); BOOST_CHECK_EQUAL(txHash, "0xcf6b53ec88659fc86e854af2e8453fa519ca261f949ef291e33c5f44ead870dc"); @@ -281,7 +281,7 @@ BOOST_AUTO_TEST_CASE(EIP4844Recover) BOOST_CHECK_EQUAL(toHex(tx.data), "3e5aa082000000000000000000000000000000000000000000000000000000000008f7060000000000000000000000000000000000000000000000000000000000168763000000000000000000000000e64a54e2533fd126c2e452c5fab544d80e2e4eb5000000000000000000000000000000000000000000000000000000000a8cc7c7000000000000000000000000000000000000000000000000000000000a8ccabe"); // clang-format on BOOST_CHECK_EQUAL(tx.getSignatureV(), tx.chainId.value() * 2 + 36); - BOOST_CHECK_EQUAL(tx.hash().hexPrefixed(), + BOOST_CHECK_EQUAL(tx.txHash().hexPrefixed(), "0x8bb97c1480b533396b0940a0f94ef5974c4989954f52d928e06e38d363bbd560"); BOOST_CHECK_EQUAL(tx.blobVersionedHashes.size(), 6); diff --git a/bcos-scheduler/test/mock/MockLedger3.h b/bcos-scheduler/test/mock/MockLedger3.h index e2b83605a3..a94681c086 100644 --- a/bcos-scheduler/test/mock/MockLedger3.h +++ b/bcos-scheduler/test/mock/MockLedger3.h @@ -128,6 +128,10 @@ class MockLedger3 : public bcos::ledger::LedgerInterface { _onGetConfig(nullptr, "0", commitBlockNumber); } + else if (_key == ledger::SYSTEM_KEY_WEB3_CHAIN_ID) + { + _onGetConfig(nullptr, "20200", commitBlockNumber); + } else if (RANGES::count(ledger::Features::featureKeys(), _key) > 0) { _onGetConfig(BCOS_ERROR_PTR(-1, "Not found!"), "0", commitBlockNumber); @@ -169,12 +173,6 @@ class MockLedger3 : public bcos::ledger::LedgerInterface _onGetList) override {} - void asyncGetStorageAt(std::string_view, std::string_view, protocol::BlockNumber, - std::function _onGetStorage) override - { - _onGetStorage(nullptr, ""); - } - void asyncPreStoreBlockTxs(bcos::protocol::ConstTransactionsPtr _blockTxs, bcos::protocol::Block::ConstPtr block, std::function _callback) override diff --git a/bcos-table/src/StateStorageFactory.h b/bcos-table/src/StateStorageFactory.h index 949baebd90..fa911ac3aa 100644 --- a/bcos-table/src/StateStorageFactory.h +++ b/bcos-table/src/StateStorageFactory.h @@ -52,7 +52,7 @@ constexpr static const std::array IGNORED_ARRAY{ bcos::storage::StorageInterface::SYS_TABLES, }; -constexpr static const std::array IGNORED_ARRAY_310{bcos::ledger::SYS_CONFIG, +constexpr static const std::array IGNORED_ARRAY_310{bcos::ledger::SYS_CONFIG, bcos::ledger::SYS_CONSENSUS, bcos::storage::StorageInterface::SYS_TABLES, bcos::ledger::SYS_CODE_BINARY, bcos::ledger::SYS_CONTRACT_ABI}; diff --git a/bcos-tars-protocol/bcos-tars-protocol/client/LedgerServiceClient.h b/bcos-tars-protocol/bcos-tars-protocol/client/LedgerServiceClient.h index d61869081e..fe683f99d9 100644 --- a/bcos-tars-protocol/bcos-tars-protocol/client/LedgerServiceClient.h +++ b/bcos-tars-protocol/bcos-tars-protocol/client/LedgerServiceClient.h @@ -57,12 +57,6 @@ class LedgerServiceClient : public bcos::ledger::LedgerInterface BCOS_LOG(ERROR) << LOG_DESC("unimplemented method asyncPreStoreBlockTxs"); } - void asyncGetStorageAt(std::string_view, std::string_view, bcos::protocol::BlockNumber, - std::function) override - { - BCOS_LOG(ERROR) << LOG_DESC("unimplemented method asyncGetStorageAt"); - } - // TODO: implement this bcos::Error::Ptr storeTransactionsAndReceipts( bcos::protocol::ConstTransactionsPtr, bcos::protocol::Block::ConstPtr) override diff --git a/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionImpl.cpp b/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionImpl.cpp index f105cff854..38a2633b7a 100644 --- a/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionImpl.cpp +++ b/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionImpl.cpp @@ -47,11 +47,17 @@ void TransactionImpl::encode(bcos::bytes& txData) const bcos::crypto::HashType TransactionImpl::hash() const { - if (m_inner()->dataHash.empty()) + if (m_inner()->dataHash.empty() && m_inner()->extraTransactionHash.empty()) { BOOST_THROW_EXCEPTION(EmptyTransactionHash{}); } + if (type() == static_cast(bcos::protocol::TransactionType::Web3Transacion)) + { + bcos::crypto::HashType hashResult((bcos::byte*)m_inner()->extraTransactionHash.data(), + m_inner()->extraTransactionHash.size()); + return hashResult; + } bcos::crypto::HashType hashResult( (bcos::byte*)m_inner()->dataHash.data(), m_inner()->dataHash.size()); @@ -60,7 +66,6 @@ bcos::crypto::HashType TransactionImpl::hash() const void bcostars::protocol::TransactionImpl::calculateHash(const bcos::crypto::Hash& hashImpl) { - // TODO: based on type to switch bcos::concepts::hash::calculate(*m_inner(), hashImpl.hasher(), m_inner()->dataHash); } diff --git a/bcos-tars-protocol/bcos-tars-protocol/tars/Transaction.tars b/bcos-tars-protocol/bcos-tars-protocol/tars/Transaction.tars index 101e407a39..bb5b2c5ae1 100644 --- a/bcos-tars-protocol/bcos-tars-protocol/tars/Transaction.tars +++ b/bcos-tars-protocol/bcos-tars-protocol/tars/Transaction.tars @@ -18,7 +18,7 @@ module bcostars { struct Transaction { 1 optional TransactionData data; - 2 optional vector dataHash; + 2 optional vector dataHash; // type == 0, hash(data); type == 1, hash(extraTransactionBytes) 3 optional vector signature; 4 optional long importTime; 5 optional int attribute; @@ -27,5 +27,6 @@ module bcostars { 8 optional string extraData; 9 optional byte type; 10 optional vector extraTransactionBytes; + 11 optional vector extraTransactionHash; // hash(rlp(10|3)) }; }; diff --git a/bcos-tool/bcos-tool/LedgerConfigFetcher.cpp b/bcos-tool/bcos-tool/LedgerConfigFetcher.cpp index 0cdb0623a9..714f11bf83 100644 --- a/bcos-tool/bcos-tool/LedgerConfigFetcher.cpp +++ b/bcos-tool/bcos-tool/LedgerConfigFetcher.cpp @@ -21,6 +21,7 @@ #include "LedgerConfigFetcher.h" #include "Exceptions.h" #include "VersionConverter.h" +#include #include #include #include @@ -325,4 +326,21 @@ void LedgerConfigFetcher::fetchNotifyRotateFlagInfo() TOOL_LOG(INFO) << LOG_DESC("fetchNotifyRotateFlagInfo success") << LOG_KV("value", std::get<0>(ret)); m_ledgerConfig->setNotifyRotateFlagInfo(boost::lexical_cast(std::get<0>(ret))); +} + +void LedgerConfigFetcher::fetchChainId() +{ + auto [chainIdStr, _] = fetchSystemConfigNoException(SYSTEM_KEY_WEB3_CHAIN_ID, {"0", 0}); + try + { + auto chainId = boost::lexical_cast(chainIdStr); + TOOL_LOG(INFO) << LOG_DESC("fetchChainId success") << LOG_KV("chainId", chainIdStr); + m_ledgerConfig->setChainId(bcos::toEvmC(chainId)); + } + catch (...) + { + TOOL_LOG(TRACE) << LOG_DESC("fetchChainId failed") << LOG_KV("chainId", chainIdStr); + evmc_uint256be chainId{}; + m_ledgerConfig->setChainId(std::move(chainId)); + } } \ No newline at end of file diff --git a/bcos-tool/bcos-tool/LedgerConfigFetcher.h b/bcos-tool/bcos-tool/LedgerConfigFetcher.h index 8b4fc61360..62bddb72c6 100644 --- a/bcos-tool/bcos-tool/LedgerConfigFetcher.h +++ b/bcos-tool/bcos-tool/LedgerConfigFetcher.h @@ -56,6 +56,7 @@ class LedgerConfigFetcher : public std::enable_shared_from_this("web3_rpc.listen_ip", "0.0.0.0"); + const std::string listenIP = _pt.get("web3_rpc.listen_ip", "127.0.0.1"); const int listenPort = _pt.get("web3_rpc.listen_port", 8545); const int threadCount = _pt.get("web3_rpc.thread_count", 8); const bool enableWeb3Rpc = _pt.get("web3_rpc.enable", false); @@ -584,6 +585,18 @@ void NodeConfig::loadChainConfig(boost::property_tree::ptree const& _pt, bool _e << LOG_KV("blockLimit", m_blockLimit); } +void NodeConfig::NodeConfig::loadWeb3ChainConfig(boost::property_tree::ptree const& _pt) +{ + m_genesisConfig.m_web3ChainID = _pt.get("web3.chain_id", "0"); + if (!isNumStr(m_genesisConfig.m_web3ChainID)) + { + BOOST_THROW_EXCEPTION( + InvalidConfig() << errinfo_comment("The web3ChainId must be number string")); + } + NodeConfig_LOG(INFO) << LOG_DESC("loadWeb3ChainConfig") + << LOG_KV("web3ChainID", m_genesisConfig.m_web3ChainID); +} + void NodeConfig::loadSecurityConfig(boost::property_tree::ptree const& _pt) { m_privateKeyPath = _pt.get("security.private_key_path", "node.pem"); diff --git a/bcos-tool/bcos-tool/NodeConfig.h b/bcos-tool/bcos-tool/NodeConfig.h index 41f52dcdbf..4d9c4208c5 100644 --- a/bcos-tool/bcos-tool/NodeConfig.h +++ b/bcos-tool/bcos-tool/NodeConfig.h @@ -281,6 +281,7 @@ class NodeConfig protected: virtual void loadChainConfig(boost::property_tree::ptree const& _pt, bool _enforceGroupId); + virtual void loadWeb3ChainConfig(boost::property_tree::ptree const& _pt); virtual void loadRpcConfig(boost::property_tree::ptree const& _pt); virtual void loadWeb3RpcConfig(boost::property_tree::ptree const& _pt); virtual void loadGatewayConfig(boost::property_tree::ptree const& _pt); diff --git a/bcos-utilities/bcos-utilities/DataConvertUtility.h b/bcos-utilities/bcos-utilities/DataConvertUtility.h index babcaf84e9..d06146cdfb 100644 --- a/bcos-utilities/bcos-utilities/DataConvertUtility.h +++ b/bcos-utilities/bcos-utilities/DataConvertUtility.h @@ -427,7 +427,8 @@ std::string toQuantity(BigNumber auto number) { return "0x0"; } - return toCompactBigEndianString(number); + auto bytes = toCompactBigEndian(number); + return toQuantity(bytes); } } // namespace bcos diff --git a/concepts/bcos-concepts/Serialize.h b/concepts/bcos-concepts/Serialize.h index d5ffbc3b7d..105a08ae7f 100644 --- a/concepts/bcos-concepts/Serialize.h +++ b/concepts/bcos-concepts/Serialize.h @@ -18,6 +18,10 @@ concept HasADLEncode = requires(Object const& object, Buffer& output) { impl_encode(object, output); }; template concept HasADLDecode = requires(Object object, const Buffer& input) { impl_decode(input, object); }; +template +concept HasADLPreEncode = requires(Object object) { impl_pre_encode(object); }; +template +concept HasADLAfterDecode = requires(Object object) { impl_after_decode(object); }; struct encode { @@ -52,10 +56,32 @@ struct decode impl_decode(input, object); } }; + +struct PreEncode +{ + template + void operator()(Object& object) const + requires HasADLPreEncode + { + impl_pre_encode(object); + } +}; + +struct AfterDecode +{ + template + void operator()(Object& object) const + requires HasADLAfterDecode + { + impl_after_decode(object); + } +}; } // namespace detail constexpr inline detail::encode encode{}; constexpr inline detail::decode decode{}; +constexpr inline detail::PreEncode pre_encode{}; +constexpr inline detail::AfterDecode after_decode{}; template concept Serializable = true; diff --git a/libinitializer/Initializer.cpp b/libinitializer/Initializer.cpp index 7ea4123496..2261a8cd74 100644 --- a/libinitializer/Initializer.cpp +++ b/libinitializer/Initializer.cpp @@ -210,6 +210,7 @@ void Initializer::init(bcos::protocol::NodeArchitectureType _nodeArchType, // build ledger auto ledger = LedgerInitializer::build(m_protocolInitializer->blockFactory(), storage, m_nodeConfig); + ledger->setKeyPageSize(m_nodeConfig->keyPageSize()); m_ledger = ledger; bcos::protocol::ExecutionMessageFactory::Ptr executionMessageFactory = nullptr; diff --git a/tools/.ci/ci_check_air.sh b/tools/.ci/ci_check_air.sh index 2322a6b653..d501c98559 100644 --- a/tools/.ci/ci_check_air.sh +++ b/tools/.ci/ci_check_air.sh @@ -232,23 +232,23 @@ LOG_INFO "======== clear node after sm test success ========" # baseline暂时不支持balance precompiled,故不测试java_sdk_demo_ci_test # baseline does not support balance precompiled temporarily, so java_sdk_demo_ci_test is not tested LOG_INFO "======== check baseline cases ========" -#init_baseline "-s" -#expand_node "-s" -#bash ${current_path}/.ci/console_ci_test.sh ${console_branch} "true" "${current_path}/nodes/127.0.0.1" -#if [[ ${?} == "0" ]]; then -# LOG_INFO "console_integrationTest success" -# else -# echo "console_integrationTest error" -# exit 1 -#fi -#bash ${current_path}/.ci/java_sdk_ci_test.sh ${console_branch} "true" "${current_path}/nodes/127.0.0.1" -#if [[ ${?} == "0" ]]; then -# LOG_INFO "java_sdk_integrationTest success" -# else -# echo "java_sdk_integrationTest error" -# exit 1 -#fi -#stop_node +init_baseline "-s" +expand_node "-s" +bash ${current_path}/.ci/console_ci_test.sh ${console_branch} "true" "${current_path}/nodes/127.0.0.1" +if [[ ${?} == "0" ]]; then + LOG_INFO "console_integrationTest success" + else + echo "console_integrationTest error" + exit 1 +fi +bash ${current_path}/.ci/java_sdk_ci_test.sh ${console_branch} "true" "${current_path}/nodes/127.0.0.1" +if [[ ${?} == "0" ]]; then + LOG_INFO "java_sdk_integrationTest success" + else + echo "java_sdk_integrationTest error" + exit 1 +fi +stop_node LOG_INFO "======== check baseline cases success ========" clear_node LOG_INFO "======== clear node after baseline test success ========" \ No newline at end of file diff --git a/tools/BcosAirBuilder/build_chain.sh b/tools/BcosAirBuilder/build_chain.sh index 0c8f65c8bf..198b6adab1 100755 --- a/tools/BcosAirBuilder/build_chain.sh +++ b/tools/BcosAirBuilder/build_chain.sh @@ -60,6 +60,7 @@ download_timeout=240 make_tar= default_group="group0" default_chainid="chain0" +default_web3_chainid="20200" use_ipv6="" # for modifying multipy ca node modify_node_path="" @@ -1720,6 +1721,9 @@ generate_genesis_config() { ; the chain id, should nerver be changed chain_id=${default_chainid} +[web3] + chain_id=${default_web3_chainid} + [consensus] ; consensus algorithm now support PBFT(consensus_type=pbft), rPBFT(consensus_type=rpbft) consensus_type=${consensus_type} diff --git a/transaction-executor/bcos-transaction-executor/vm/EVMHostInterface.h b/transaction-executor/bcos-transaction-executor/vm/EVMHostInterface.h index 213f68de70..65c8927708 100644 --- a/transaction-executor/bcos-transaction-executor/vm/EVMHostInterface.h +++ b/transaction-executor/bcos-transaction-executor/vm/EVMHostInterface.h @@ -155,7 +155,7 @@ struct EVMHostInterface .block_timestamp = hostContext.timestamp(), .block_gas_limit = hostContext.blockGasLimit(), .block_prev_randao = {}, - .chain_id = {}, + .chain_id = hostContext.chainId(), .block_base_fee = {}, }; return result; diff --git a/transaction-executor/bcos-transaction-executor/vm/HostContext.h b/transaction-executor/bcos-transaction-executor/vm/HostContext.h index 2ca9a7c99a..9b66fffd5c 100644 --- a/transaction-executor/bcos-transaction-executor/vm/HostContext.h +++ b/transaction-executor/bcos-transaction-executor/vm/HostContext.h @@ -280,6 +280,7 @@ class HostContext : public evmc_host_context int64_t timestamp() const { return m_blockHeader.timestamp(); } evmc_address const& origin() const { return m_origin; } int64_t blockGasLimit() const { return std::get<0>(m_ledgerConfig.gasLimit()); } + evmc_uint256be chainId() const { return m_ledgerConfig.chainId().value_or(evmc_uint256be{}); } /// Revert any changes made (by any of the other calls). void log(const evmc_address& address, h256s topics, bytesConstRef data) From f8802d5cf81c297801b850dadb214826b45245ca Mon Sep 17 00:00:00 2001 From: JDD <1950618179@qq.com> Date: Fri, 26 Apr 2024 10:55:15 +0800 Subject: [PATCH 16/39] (rpc): add filter, log matcher and filter request. (#4403) Co-authored-by: jdkuang --- .../protocol/TransactionReceipt.h | 2 +- bcos-rpc/bcos-rpc/Common.h | 7 + bcos-rpc/bcos-rpc/filter/Common.h | 29 ++ bcos-rpc/bcos-rpc/filter/Filter.h | 66 +++++ bcos-rpc/bcos-rpc/filter/FilterRequest.cpp | 133 +++++++++ bcos-rpc/bcos-rpc/filter/FilterRequest.h | 101 +++++++ bcos-rpc/bcos-rpc/filter/LogMatcher.cpp | 89 ++++++ bcos-rpc/bcos-rpc/filter/LogMatcher.h | 33 +++ .../bcos-rpc/jsonrpc/JsonRpcFilterSystem.h | 35 +++ bcos-rpc/bcos-rpc/util.cpp | 54 ++++ .../model/FilterRequest.h => util.h} | 17 +- .../web3jsonrpc/endpoints/EthEndpoint.cpp | 33 +-- .../web3jsonrpc/model/FilterRequest.cpp | 21 -- .../web3jsonrpc/model/ReceiptResponse.h | 2 +- .../web3jsonrpc/model/Web3FilterRequest.h | 55 ++++ bcos-rpc/bcos-rpc/web3jsonrpc/utils/Common.h | 9 +- bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp | 259 ++++++++++++++++++ .../protocol/TransactionReceiptImpl.cpp | 13 + .../protocol/TransactionReceiptImpl.h | 2 +- 19 files changed, 893 insertions(+), 67 deletions(-) create mode 100644 bcos-rpc/bcos-rpc/filter/Common.h create mode 100644 bcos-rpc/bcos-rpc/filter/Filter.h create mode 100644 bcos-rpc/bcos-rpc/filter/FilterRequest.cpp create mode 100644 bcos-rpc/bcos-rpc/filter/FilterRequest.h create mode 100644 bcos-rpc/bcos-rpc/filter/LogMatcher.cpp create mode 100644 bcos-rpc/bcos-rpc/filter/LogMatcher.h create mode 100644 bcos-rpc/bcos-rpc/jsonrpc/JsonRpcFilterSystem.h create mode 100644 bcos-rpc/bcos-rpc/util.cpp rename bcos-rpc/bcos-rpc/{web3jsonrpc/model/FilterRequest.h => util.h} (78%) delete mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/model/FilterRequest.cpp create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3FilterRequest.h diff --git a/bcos-framework/bcos-framework/protocol/TransactionReceipt.h b/bcos-framework/bcos-framework/protocol/TransactionReceipt.h index 48e92493de..b16e4d54c1 100644 --- a/bcos-framework/bcos-framework/protocol/TransactionReceipt.h +++ b/bcos-framework/bcos-framework/protocol/TransactionReceipt.h @@ -47,7 +47,7 @@ class TransactionReceipt virtual int32_t status() const = 0; virtual bcos::bytesConstRef output() const = 0; virtual gsl::span logEntries() const = 0; - virtual LogEntries&& takeLogEntris() = 0; + virtual LogEntries&& takeLogEntries() = 0; virtual protocol::BlockNumber blockNumber() const = 0; virtual std::string_view effectiveGasPrice() const = 0; virtual void setEffectiveGasPrice(std::string effectiveGasPrice) = 0; diff --git a/bcos-rpc/bcos-rpc/Common.h b/bcos-rpc/bcos-rpc/Common.h index 34e85dbad4..e8a78bae03 100644 --- a/bcos-rpc/bcos-rpc/Common.h +++ b/bcos-rpc/bcos-rpc/Common.h @@ -54,4 +54,11 @@ class JsonSink bcos::bytes& m_buffer; }; + +constexpr const std::string_view EarliestBlock{"earliest"}; +constexpr const std::string_view LatestBlock{"latest"}; +constexpr const std::string_view PendingBlock{"pending"}; +constexpr const std::string_view SafeBlock{"safe"}; +constexpr const std::string_view FinalizedBlock{"finalized"}; + } // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/filter/Common.h b/bcos-rpc/bcos-rpc/filter/Common.h new file mode 100644 index 0000000000..8850302390 --- /dev/null +++ b/bcos-rpc/bcos-rpc/filter/Common.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +// The largest number of topic in one event log +#define EVENT_LOG_TOPICS_MAX_INDEX (4) + +#define FILTER_LOG(LEVEL) BCOS_LOG(LEVEL) << "[FILTER]" + +namespace bcos +{ +namespace rpc +{ + +enum SubscriptionType +{ + // LogsSubscription queries for new + LogsSubscription = 0, + // PendingTransactionsSubscription queries for pending transactions entering the pending state + PendingTransactionsSubscription, + // BlocksSubscription queries hashes for blocks that are imported + BlocksSubscription, + // LastIndexSubscription keeps track of the last index + LastIndexSubscription +}; + +} // namespace rpc +} // namespace bcos diff --git a/bcos-rpc/bcos-rpc/filter/Filter.h b/bcos-rpc/bcos-rpc/filter/Filter.h new file mode 100644 index 0000000000..2e78621b82 --- /dev/null +++ b/bcos-rpc/bcos-rpc/filter/Filter.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace bcos +{ +namespace rpc +{ + +class Filter +{ +public: + using Ptr = std::shared_ptr; + + Filter(SubscriptionType type, uint64_t id, FilterRequest::Ptr params, bool fullTx, + bcos::protocol::BlockNumber startBlockNumber, boost::asio::io_service& ioService, + int timeout, std::string_view group, std::string_view node) + : m_type(type), + m_id(id), + m_params(params), + m_fullTx(fullTx), + m_startBlockNumber(startBlockNumber), + m_deadline(ioService, boost::posix_time::seconds(timeout)), + m_group(group), + m_nodeName(node) + {} + + virtual ~Filter() { cancelDeadLineTimer(); } + + SubscriptionType type() const { return m_type; } + uint64_t id() const { return m_id; } + int64_t startBlockNumber() const { return m_startBlockNumber.load(); } + FilterRequest::Ptr params() const { return m_params; } + bool fullTx() const { return m_fullTx; } + boost::asio::deadline_timer& deadline() { return m_deadline; } + std::string_view group() const { return m_group; } + std::string_view nodeName() const { return m_nodeName; } + + void setStartBlockNumber(int64_t number) { m_startBlockNumber.store(number); } + void cancelDeadLineTimer() { m_deadline.cancel(); } + void setId(uint64_t id) { m_id = id; } + + bool checkGroupAndNode(std::string_view group, std::string_view node) const + { + return m_group == group && m_nodeName == node; + } + +private: + SubscriptionType m_type; + uint64_t m_id; + FilterRequest::Ptr m_params; + bool m_fullTx; + std::atomic m_startBlockNumber; + boost::asio::deadline_timer m_deadline; + std::string m_group; + std::string m_nodeName; +}; + +} // namespace rpc +} // namespace bcos \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/filter/FilterRequest.cpp b/bcos-rpc/bcos-rpc/filter/FilterRequest.cpp new file mode 100644 index 0000000000..aea5e1aa19 --- /dev/null +++ b/bcos-rpc/bcos-rpc/filter/FilterRequest.cpp @@ -0,0 +1,133 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file FilterRequest.cpp + * @author: kyonGuo + * @date 2024/4/11 + */ +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::rpc; + +static inline std::string removePrefix(const std::string& str) +{ + if ((str.compare(0, 2, "0x") == 0) || (str.compare(0, 2, "0X") == 0)) + { + return str.substr(2); + } + return str; +} + +void FilterRequest::fromJson(const Json::Value& jParams, protocol::BlockNumber latest) +{ + // default: LatestBlockNumber + m_fromBlock = latest; + if (jParams.isMember("fromBlock") && !jParams["fromBlock"].isNull()) + { + auto [blockNumber, isLatest] = getBlockNumberByTag(latest, jParams["fromBlock"].asString()); + m_fromBlock = blockNumber; + m_fromIsLatest = isLatest; + } + + // default: LatestBlockNumber + m_toBlock = latest; + if (jParams.isMember("toBlock") && !jParams["toBlock"].isNull()) + { + auto [blockNumber, isLatest] = getBlockNumberByTag(latest, jParams["toBlock"].asString()); + m_toBlock = blockNumber; + m_toIsLatest = isLatest; + } + + if (jParams.isMember("address") && !jParams["address"].isNull()) + { + auto& jAddresses = jParams["address"]; + if (jAddresses.isArray()) + { + for (Json::Value::ArrayIndex index = 0; index < jAddresses.size(); ++index) + { + addAddress(removePrefix(jAddresses[index].asString())); + } + } + else + { + addAddress(removePrefix(jAddresses.asString())); + } + } + + if (jParams.isMember("topics") && !jParams["topics"].isNull()) + { + auto& jTopics = jParams["topics"]; + + // check number of topic + if (jTopics.size() > EVENT_LOG_TOPICS_MAX_INDEX) + { + FILTER_LOG(ERROR) << LOG_BADGE("fromJson") << LOG_DESC("exceed max topics") + << LOG_KV("topicsSize", jTopics.size()); + BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParamsCode(), "exceed max topics")); + } + resizeTopic(jTopics.size()); + for (Json::Value::ArrayIndex index = 0; index < jTopics.size(); ++index) + { + auto& jIndex = jTopics[index]; + if (jIndex.isNull()) + { + continue; + } + + if (jIndex.isArray()) + { // array topics + for (Json::Value::ArrayIndex innerIndex = 0; innerIndex < jIndex.size(); + ++innerIndex) + { + addTopic(index, removePrefix(jIndex[innerIndex].asString())); + } + } + else + { // single topic, string value + addTopic(index, removePrefix(jIndex.asString())); + } + } + } + + if (jParams.isMember("blockhash") && !jParams["blockhash"].isNull()) + { + m_blockHash = jParams["blockhash"].asString(); + } +} + +bool FilterRequest::checkBlockRange() +{ + if (fromBlock() < 0 || toBlock() < 0) + { + return false; + } + if (fromIsLatest() && toIsLatest()) + { + return true; + } + if (!fromIsLatest() && !toIsLatest() && toBlock() >= fromBlock()) + { + return true; + } + if (!fromIsLatest() && toIsLatest()) + { + return true; + } + return false; +} \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/filter/FilterRequest.h b/bcos-rpc/bcos-rpc/filter/FilterRequest.h new file mode 100644 index 0000000000..ad6094d403 --- /dev/null +++ b/bcos-rpc/bcos-rpc/filter/FilterRequest.h @@ -0,0 +1,101 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file FilterRequest.h + * @author: kyonGuo + * @date 2024/4/11 + */ + +#pragma once +#include +#include +#include +#include +#include + +namespace bcos::rpc +{ + +class FilterRequest +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + + FilterRequest() = default; + virtual ~FilterRequest() = default; + + FilterRequest(const FilterRequest& other) + { + if (&other == this) + { + return; + } + m_fromBlock = other.m_fromBlock; + m_toBlock = other.m_toBlock; + m_fromIsLatest = other.m_fromIsLatest; + m_toIsLatest = other.m_toIsLatest; + m_addresses = other.m_addresses; + m_topics = other.m_topics; + m_blockHash = other.m_blockHash; + } + +public: + int64_t fromBlock() const { return m_fromBlock; } + int64_t toBlock() const { return m_toBlock; } + const std::set& addresses() const { return m_addresses; } + std::set& addresses() { return m_addresses; } + const std::vector>& topics() const { return m_topics; } + std::vector>& topics() { return m_topics; } + std::string& blockHash() { return m_blockHash; } + const std::string& blockHash() const { return m_blockHash; } + bool fromIsLatest() const { return m_fromIsLatest; } + bool toIsLatest() const { return m_toIsLatest; } + + void setFromBlock(int64_t _fromBlock) { m_fromBlock = _fromBlock; } + void setToBlock(int64_t _toBlock) { m_toBlock = _toBlock; } + void addAddress(const std::string& _address) { m_addresses.insert(_address); } + void resizeTopic(size_t size) { m_topics.resize(size); } + void addTopic(std::size_t _index, const std::string& _topic) + { + m_topics[_index].insert(_topic); + } + void setBlockHash(const std::string& _hash) { m_blockHash = _hash; } + void fromJson(const Json::Value& jParams, protocol::BlockNumber latest); + bool checkBlockRange(); + + virtual int32_t InvalidParamsCode() = 0; + +protected: + bcos::protocol::BlockNumber m_fromBlock = 0; + bcos::protocol::BlockNumber m_toBlock = 0; + bool m_fromIsLatest = true; + bool m_toIsLatest = true; + std::set m_addresses; + std::vector> m_topics; + std::string m_blockHash; +}; + +class FilterRequestFactory +{ +public: + using Ptr = std::shared_ptr; + FilterRequestFactory() = default; + virtual ~FilterRequestFactory() = default; + virtual FilterRequest::Ptr create() = 0; + virtual FilterRequest::Ptr create(const FilterRequest& req) = 0; +}; + +} // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/filter/LogMatcher.cpp b/bcos-rpc/bcos-rpc/filter/LogMatcher.cpp new file mode 100644 index 0000000000..70b53576db --- /dev/null +++ b/bcos-rpc/bcos-rpc/filter/LogMatcher.cpp @@ -0,0 +1,89 @@ +#include +#include +#include + +using namespace bcos; +using namespace bcos::rpc; + +uint32_t LogMatcher::matches( + FilterRequest::ConstPtr _params, bcos::protocol::Block::ConstPtr _block, Json::Value& _result) +{ + uint32_t count = 0; + for (std::size_t index = 0; index < _block->transactionsMetaDataSize(); index++) + { + count += matches(_params, _block->blockHeaderConst()->hash(), _block->receipt(index), + _block->transactionMetaData(index), index, _result); + } + + return count; +} + +uint32_t LogMatcher::matches(FilterRequest::ConstPtr _params, bcos::crypto::HashType&& _blockHash, + bcos::protocol::TransactionReceipt::ConstPtr&& _receipt, + bcos::protocol::TransactionMetaData::ConstPtr&& _tx, std::size_t _txIndex, Json::Value& _result) +{ + uint32_t count = 0; + auto blockNumber = _receipt->blockNumber(); + auto mutableReceipt = const_cast(_receipt.get()); + auto logEntries = mutableReceipt->takeLogEntries(); + for (size_t i = 0; i < logEntries.size(); i++) + { + const auto& logEntry = logEntries[i]; + if (matches(_params, logEntry)) + { + count++; + Json::Value log; + log["data"] = toHexStringWithPrefix(logEntry.data()); + log["logIndex"] = toQuantity(i); + log["blockNumber"] = toQuantity(blockNumber); + log["blockHash"] = _blockHash.hexPrefixed(); + log["transactionIndex"] = toQuantity(_txIndex); + log["transactionHash"] = _tx->hash().hexPrefixed(); + log["removed"] = false; + log["address"] = "0x" + std::string(logEntry.address()); + Json::Value jTopics(Json::arrayValue); + for (const auto& topic : logEntry.topics()) + { + jTopics.append(topic.hexPrefixed()); + } + log["topics"] = std::move(jTopics); + _result.append(std::move(log)); + } + } + return count; +} + +bool LogMatcher::matches(FilterRequest::ConstPtr _params, const bcos::protocol::LogEntry& _logEntry) +{ + const auto& addresses = _params->addresses(); + const auto& topics = _params->topics(); + const auto& logTopics = _logEntry.topics(); + + FILTER_LOG(TRACE) << LOG_BADGE("matches") << LOG_KV("address", _logEntry.address()) + << LOG_KV("logEntry topics", _logEntry.topics().size()); + + // An empty address array matches all values otherwise log.address must be in addresses + if (!addresses.empty() && !addresses.count(std::string(_logEntry.address()))) + { + return false; + } + + if (topics.size() > logTopics.size()) + { + return false; + } + + for (size_t i = 0; i < topics.size(); ++i) + { + const auto& sub = topics[i]; + if (sub.empty()) + { + continue; + } + if (!sub.contains(logTopics[i].hex())) + { + return false; + } + } + return true; +} diff --git a/bcos-rpc/bcos-rpc/filter/LogMatcher.h b/bcos-rpc/bcos-rpc/filter/LogMatcher.h new file mode 100644 index 0000000000..378ef1417b --- /dev/null +++ b/bcos-rpc/bcos-rpc/filter/LogMatcher.h @@ -0,0 +1,33 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace rpc +{ +class LogMatcher +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + ~LogMatcher() {} + +public: + bool matches( + FilterRequest::ConstPtr _params, const bcos::protocol::LogEntry& _logEntry); + + uint32_t matches(FilterRequest::ConstPtr _params, bcos::crypto::HashType&& _blockHash, + bcos::protocol::TransactionReceipt::ConstPtr&& _receipt, + bcos::protocol::TransactionMetaData::ConstPtr&& _tx, std::size_t _txIndex, Json::Value& _result); + + uint32_t matches( + FilterRequest::ConstPtr _params, bcos::protocol::Block::ConstPtr _block, Json::Value& _result); +}; + +} // namespace rpc +} // namespace bcos diff --git a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcFilterSystem.h b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcFilterSystem.h new file mode 100644 index 0000000000..f2592731af --- /dev/null +++ b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcFilterSystem.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +namespace bcos +{ +namespace rpc +{ + +class JsonRpcFilterRequest : public FilterRequest +{ +public: + JsonRpcFilterRequest() = default; + ~JsonRpcFilterRequest() = default; + JsonRpcFilterRequest(const FilterRequest& oth) : FilterRequest(oth) {} + virtual int32_t InvalidParamsCode() override { return JsonRpcError::InvalidParams; } +}; + +class JsonRpcFilterRequestFactory : public FilterRequestFactory +{ +public: + JsonRpcFilterRequestFactory() = default; + ~JsonRpcFilterRequestFactory() = default; + FilterRequest::Ptr create() override { return std::make_shared(); } + FilterRequest::Ptr create(const FilterRequest& req) override + { + return std::make_shared(req); + } +}; + +// class JsonRpcFilterSystem; + +} // namespace rpc +} // namespace bcos \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/util.cpp b/bcos-rpc/bcos-rpc/util.cpp new file mode 100644 index 0000000000..3d8ded0329 --- /dev/null +++ b/bcos-rpc/bcos-rpc/util.cpp @@ -0,0 +1,54 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file util.cpp + * @author: jdkuang + * @date 2024/4/24 + */ + +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::rpc; + +// return (actual block number, isLatest block) +std::tuple bcos::rpc::getBlockNumberByTag( + protocol::BlockNumber latest, std::string_view blockTag) +{ + if (blockTag == EarliestBlock) + { + return std::make_tuple(0, false); + } + else if (blockTag == LatestBlock || blockTag == SafeBlock || blockTag == FinalizedBlock || + blockTag == PendingBlock) + { + return std::make_tuple(latest, true); + } + else + { + static const std::regex hexRegex("^0x[0-9a-fA-F]+$"); + if (std::regex_match(blockTag.data(), hexRegex)) + { + auto blockNumber = fromQuantity(std::string(blockTag)); + return std::make_tuple(blockNumber, false); + } + BOOST_THROW_EXCEPTION( + JsonRpcException(InvalidParams, "Invalid Block Number: " + std::string(blockTag))); + } +} \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/FilterRequest.h b/bcos-rpc/bcos-rpc/util.h similarity index 78% rename from bcos-rpc/bcos-rpc/web3jsonrpc/model/FilterRequest.h rename to bcos-rpc/bcos-rpc/util.h index 43673eb1ce..7a594f8b3e 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/FilterRequest.h +++ b/bcos-rpc/bcos-rpc/util.h @@ -13,22 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. * - * @file FilterRequest.h - * @author: kyonGuo - * @date 2024/4/11 + * @file util.h + * @author: jdkuang + * @date 2024/4/24 */ #pragma once #include -#include -#include -#include +#include namespace bcos::rpc { - -class FilterRequest -{ -}; - +std::tuple getBlockNumberByTag( + protocol::BlockNumber latest, std::string_view blockTag); } // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp index 87414b2af6..9246516e86 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -668,33 +669,11 @@ task::Task EthEndpoint::getLogs(const Json::Value&, Json::Value&) co_return; } - -// return (actual block number, isLatest block) task::Task> EthEndpoint::getBlockNumberByTag( std::string_view blockTag) { - if (blockTag == EarliestBlock) - { - co_return std::make_tuple(0, false); - } - else if (blockTag == LatestBlock || blockTag == SafeBlock || blockTag == FinalizedBlock || - blockTag == PendingBlock) - { - auto ledger = m_nodeService->ledger(); - auto number = co_await ledger::getCurrentBlockNumber(*ledger); - co_return std::make_tuple(number, true); - } - else - { - static const std::regex hexRegex("^0x[0-9a-fA-F]+$"); - if (std::regex_match(blockTag.data(), hexRegex)) - { - auto ledger = m_nodeService->ledger(); - auto number = co_await ledger::getCurrentBlockNumber(*ledger); - auto blockNumber = fromQuantity(std::string(blockTag)); - co_return std::make_tuple(blockNumber, std::cmp_equal(number, blockNumber)); - } - BOOST_THROW_EXCEPTION( - JsonRpcException(InvalidParams, "Invalid Block Number: " + std::string(blockTag))); - } -} + auto ledger = m_nodeService->ledger(); + auto latest = co_await ledger::getCurrentBlockNumber(*ledger); + auto [number, _] = bcos::rpc::getBlockNumberByTag(latest, blockTag); + co_return std::make_tuple(number, std::cmp_equal(latest, number)); +} \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/FilterRequest.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/model/FilterRequest.cpp deleted file mode 100644 index f5aa185ade..0000000000 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/FilterRequest.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (C) 2024 FISCO BCOS. - * SPDX-License-Identifier: Apache-2.0 - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @file FilterRequest.cpp - * @author: kyonGuo - * @date 2024/4/11 - */ - -#include "FilterRequest.h" diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h index 9592fe8d3b..e34cc067d2 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h @@ -92,7 +92,7 @@ namespace bcos::rpc } result["logs"] = Json::arrayValue; auto mutableReceipt = const_cast(receipt.get()); - auto receiptLog = mutableReceipt->takeLogEntris(); + auto receiptLog = mutableReceipt->takeLogEntries(); for (size_t i = 0; i < receiptLog.size(); i++) { Json::Value log; diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3FilterRequest.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3FilterRequest.h new file mode 100644 index 0000000000..d73ad29d96 --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3FilterRequest.h @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file FilterRequest.h + * @author: kyonGuo + * @date 2024/4/11 + */ + +#pragma once +#include +#include + +namespace bcos::rpc +{ + +class Web3FilterRequest : public FilterRequest +{ +public: + Web3FilterRequest() = default; + ~Web3FilterRequest() = default; + Web3FilterRequest(const FilterRequest& oth) : FilterRequest(oth) {} + virtual int32_t InvalidParamsCode() override + { + return Web3JsonRpcError::Web3DefautError; + } +}; + +class Web3FilterRequestFactory : public FilterRequestFactory +{ +public: + Web3FilterRequestFactory() = default; + ~Web3FilterRequestFactory() = default; + FilterRequest::Ptr create() override + { + return std::make_shared(); + } + FilterRequest::Ptr create(const FilterRequest& req) override + { + return std::make_shared(req); + } +}; + +} // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/utils/Common.h b/bcos-rpc/bcos-rpc/web3jsonrpc/utils/Common.h index 6d0d089cd4..b3fcaef4ce 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/utils/Common.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/utils/Common.h @@ -26,9 +26,8 @@ namespace bcos::rpc { constexpr const uint64_t LowestGasPrice{21000}; -constexpr const std::string_view EarliestBlock{"earliest"}; -constexpr const std::string_view LatestBlock{"latest"}; -constexpr const std::string_view PendingBlock{"pending"}; -constexpr const std::string_view SafeBlock{"safe"}; -constexpr const std::string_view FinalizedBlock{"finalized"}; +enum Web3JsonRpcError : int32_t +{ + Web3DefautError = -32000, +}; } // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp index 0120cedd09..80dabfc226 100644 --- a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp +++ b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp @@ -42,6 +42,10 @@ #include #include +#include +#include +#include + using namespace bcos; using namespace bcos::rpc; using namespace bcos::crypto; @@ -436,5 +440,260 @@ BOOST_AUTO_TEST_CASE(handleEIP4844TxTest) "0xa8fd95a70b4f2b6cea8c52bcb782b5c3f806a0d5250cf75a1d97a6e899f09979"); // clang-format on } +BOOST_AUTO_TEST_CASE(logMatcherTest) +{ + // clang-format off + h256 A(fromHexWithPrefix(std::string("0x7ac7a04970319ae8fc5b92fe177d000fee3c00c92f8e78aae13d6571f17c351f"))); + h256 B(fromHexWithPrefix(std::string("0x1fe46fe782fa5618721cdf61de2e50c0639f4b26f6568f9c67b128f5610ced68"))); + h256 C(fromHexWithPrefix(std::string("0x0c5ace674d8e621803f806d73d9d03a49b2694f13186b8d657c9df689fe4e478"))); + h256 D(fromHexWithPrefix(std::string("0xf0173383cebedea9e486292201bc460817279651f0b05c46c0ad1f5e094e62d9"))); + h256 E(fromHexWithPrefix(std::string("0x7b0682ce692786e3ad67f5ff45e7fe63c1dbed629f5f016c605b814d905b6447"))); + h256 F(fromHexWithPrefix(std::string("0xa0f422234f24bda551aee9753bb806b078efa605b128061af1be03e7ce54e64d"))); + h256 G(fromHexWithPrefix(std::string("0xba2b5bce04969801bc79406f18a929f465e83afbb138834d5ef55a0112018a8f"))); + h256 H(fromHexWithPrefix(std::string("0x8518c89a1be62e684eac2fcfad1682ea6fbccff0e436fb7c8ce19c64ad569f79"))); + bytes address1(fromHexWithPrefix(std::string("0x6849f21d1e455e9f0712b1e99fa4fcd23758e8f1"))); + bytes address2(fromHexWithPrefix(std::string("0xc75ebd24f8ae4f17be209b30dc8dea4fadcf67ce"))); + // clang-format on + + LogMatcher matcher; + + // 1.[] "anything" + { + // [A, B] + protocol::LogEntry log1(address1, {A, B}, bytes()); + // [C, D] + protocol::LogEntry log2(address1, {C, D}, bytes()); + auto params1 = std::make_shared(); + auto params2 = std::make_shared(); + params2->addAddress(*toHexString(address1)); + BOOST_CHECK(matcher.matches(params1, log1)); + BOOST_CHECK(matcher.matches(params1, log2)); + BOOST_CHECK(!matcher.matches(params2, log1)); + BOOST_CHECK(!matcher.matches(params2, log2)); + } + // 2.[A] "A in first position (and anything after)" + { + auto params = std::make_shared(); + params->resizeTopic(1); + params->addTopic(0, *toHexString(A)); + protocol::LogEntry log; + // [A] + log = protocol::LogEntry(address1, {A}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + // [A, B] + log = protocol::LogEntry(address1, {A, B}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + // [C] + log = protocol::LogEntry(address1, {C}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [C, D] + log = protocol::LogEntry(address1, {C}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + } + // 3.[null, B] "anything in first position AND B in second position (and anything after)" + { + auto params = std::make_shared(); + params->resizeTopic(2); + params->addTopic(1, *toHexString(B)); + protocol::LogEntry log; + // matched + // [A, B] + log = protocol::LogEntry(address1, {A, B}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + // [C, B] + log = protocol::LogEntry(address1, {C, B}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + // [A, B, F] + log = protocol::LogEntry(address1, {A, B, F}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + // [A, B, E] + log = protocol::LogEntry(address1, {A, B, E}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + // [C, B, F] + log = protocol::LogEntry(address1, {C, B, F}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + // [C, B, E] + log = protocol::LogEntry(address1, {C, B, E}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + // mismatched + // [A, G] + log = protocol::LogEntry(address1, {A, G}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [C, G] + log = protocol::LogEntry(address1, {C, G}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [A, G, F] + log = protocol::LogEntry(address1, {A, G, F}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [A, G, E] + log = protocol::LogEntry(address1, {A, G, E}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [C, G, F] + log = protocol::LogEntry(address1, {C, G, F}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [C, G, E] + log = protocol::LogEntry(address1, {C, G, E}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + } + // 4.[A, B] "A in first position AND B in second position (and anything after)" + { + auto params = std::make_shared(); + params->resizeTopic(2); + params->addTopic(0, *toHexString(A)); + params->addTopic(1, *toHexString(B)); + protocol::LogEntry log; + + // matched + // [A, B] + log = protocol::LogEntry(address1, {A, B}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + // [A, B, C] + log = protocol::LogEntry(address1, {A, B, C}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + // [A, B, E] + log = protocol::LogEntry(address1, {A, B, E}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + + // mismatched + // [A] + log = protocol::LogEntry(address1, {A}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [A, C] + log = protocol::LogEntry(address1, {A, C}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [A, C, F] + log = protocol::LogEntry(address1, {A, C, F}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [B] + log = protocol::LogEntry(address1, {B}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [B, C] + log = protocol::LogEntry(address1, {B, C}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [B, C, F] + log = protocol::LogEntry(address1, {B, C, F}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + } + // 5.[[A, B], [A, B]] "(A OR B) in first position AND (A OR B) in second position (and anything + // after)" + { + auto params = std::make_shared(); + params->resizeTopic(2); + params->addTopic(0, *toHexString(A)); + params->addTopic(0, *toHexString(B)); + params->addTopic(1, *toHexString(C)); + params->addTopic(1, *toHexString(D)); + protocol::LogEntry log; + + // matched + { + // [A, C] + log = protocol::LogEntry(address1, {A, C}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + // [A, D] + log = protocol::LogEntry(address1, {A, D}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + // [B, C] + log = protocol::LogEntry(address1, {B, C}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + // [B, D] + log = protocol::LogEntry(address1, {B, D}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + } + { + // [A, C, E] + log = protocol::LogEntry(address1, {A, C, E}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + // [A, D, E] + log = protocol::LogEntry(address1, {A, D, E}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + // [B, C, E] + log = protocol::LogEntry(address1, {B, C, E}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + // [B, D, E] + log = protocol::LogEntry(address1, {B, D, E}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + } + { + // [A, C, F] + log = protocol::LogEntry(address1, {A, C, F}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + // [A, D, F] + log = protocol::LogEntry(address1, {A, D, F}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + // [B, C, F] + log = protocol::LogEntry(address1, {B, C, F}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + // [B, D, F] + log = protocol::LogEntry(address1, {B, D, F}, bytes()); + BOOST_CHECK(matcher.matches(params, log)); + } + // mismatched + { + // [A] + log = protocol::LogEntry(address1, {A}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [B] + log = protocol::LogEntry(address1, {B}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + } + { + // [E, C] + log = protocol::LogEntry(address1, {E, C}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [E, D] + log = protocol::LogEntry(address1, {E, D}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [F, C] + log = protocol::LogEntry(address1, {F, C}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [F, D] + log = protocol::LogEntry(address1, {F, D}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + } + { + // [A, E] + log = protocol::LogEntry(address1, {A, E}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [A, F] + log = protocol::LogEntry(address1, {A, F}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [B, E] + log = protocol::LogEntry(address1, {B, E}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [B, F] + log = protocol::LogEntry(address1, {B, F}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + } + { + // [E, C, G] + log = protocol::LogEntry(address1, {E, C, G}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [E, D, G] + log = protocol::LogEntry(address1, {E, D, G}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [F, C, G] + log = protocol::LogEntry(address1, {F, C, G}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [F, D, G] + log = protocol::LogEntry(address1, {F, D, G}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + } + { + // [A, E, G] + log = protocol::LogEntry(address1, {A, E, G}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [A, F, G] + log = protocol::LogEntry(address1, {A, F, G}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [B, E, G] + log = protocol::LogEntry(address1, {B, E, G}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + // [B, F, G] + log = protocol::LogEntry(address1, {B, F, G}, bytes()); + BOOST_CHECK(!matcher.matches(params, log)); + } + } +} BOOST_AUTO_TEST_SUITE_END() } // namespace bcos::test \ No newline at end of file diff --git a/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.cpp b/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.cpp index 0103c5e5ec..463ff94006 100644 --- a/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.cpp +++ b/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.cpp @@ -98,6 +98,19 @@ gsl::span bcostars::protocol::TransactionReceipt return {m_logEntries.data(), m_logEntries.size()}; } +bcos::protocol::LogEntries&& bcostars::protocol::TransactionReceiptImpl::takeLogEntries() +{ + if (m_logEntries.empty()) + { + m_logEntries.reserve(m_inner()->data.logEntries.size()); + for (auto& it : m_inner()->data.logEntries) + { + auto bcosLogEntry = toBcosLogEntry(it); + m_logEntries.emplace_back(std::move(bcosLogEntry)); + } + } + return std::move(m_logEntries); +} bcos::protocol::BlockNumber bcostars::protocol::TransactionReceiptImpl::blockNumber() const { return m_inner()->data.blockNumber; diff --git a/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.h b/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.h index 100d51b59a..f5f8fc1d19 100644 --- a/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.h +++ b/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.h @@ -65,7 +65,7 @@ class TransactionReceiptImpl : public bcos::protocol::TransactionReceipt int32_t status() const override; bcos::bytesConstRef output() const override; gsl::span logEntries() const override; - bcos::protocol::LogEntries&& takeLogEntris() override { return std::move(m_logEntries); } + bcos::protocol::LogEntries&& takeLogEntries() override; bcos::protocol::BlockNumber blockNumber() const override; std::string_view effectiveGasPrice() const override; void setEffectiveGasPrice(std::string effectiveGasPrice) override; From 2b86175fc3b266b62385d5b5c300001c5080b078 Mon Sep 17 00:00:00 2001 From: JDD <1950618179@qq.com> Date: Wed, 8 May 2024 19:58:43 +0800 Subject: [PATCH 17/39] (rpc): implement ethereum filter related interfaces (newBlockFilter, newPendingTxFilter, newFilter, uninstallFilter, getFilterChanges, getFilterLogs, getLogs) (#4412) Co-authored-by: jdkuang --- bcos-rpc/bcos-rpc/filter/Common.h | 6 + bcos-rpc/bcos-rpc/filter/Filter.h | 32 +-- bcos-rpc/bcos-rpc/filter/FilterRequest.cpp | 27 +- bcos-rpc/bcos-rpc/filter/FilterRequest.h | 6 +- bcos-rpc/bcos-rpc/filter/FilterSystem.cpp | 304 ++++++++++++++++++++ bcos-rpc/bcos-rpc/filter/FilterSystem.h | 196 +++++++++++++ bcos-rpc/bcos-rpc/filter/LogMatcher.cpp | 12 +- bcos-rpc/bcos-rpc/filter/LogMatcher.h | 11 +- bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp | 19 +- 9 files changed, 549 insertions(+), 64 deletions(-) create mode 100644 bcos-rpc/bcos-rpc/filter/FilterSystem.cpp create mode 100644 bcos-rpc/bcos-rpc/filter/FilterSystem.h diff --git a/bcos-rpc/bcos-rpc/filter/Common.h b/bcos-rpc/bcos-rpc/filter/Common.h index 8850302390..4a7a3883d6 100644 --- a/bcos-rpc/bcos-rpc/filter/Common.h +++ b/bcos-rpc/bcos-rpc/filter/Common.h @@ -25,5 +25,11 @@ enum SubscriptionType LastIndexSubscription }; +// Trigger a filter cleanup operation every 3s +static constexpr const uint64_t CLEANUP_FILTER_TIME = 3000; +static constexpr const uint64_t MAX_TRAVERSE_FILTERS_COUNT = 10000; +// the filter expiration time, default is 5 minutes +static constexpr const uint64_t FILTER_DEFAULT_EXPIRATION_TIME = uint64_t(60 * 5 * 1000); + } // namespace rpc } // namespace bcos diff --git a/bcos-rpc/bcos-rpc/filter/Filter.h b/bcos-rpc/bcos-rpc/filter/Filter.h index 2e78621b82..ba3e622b95 100644 --- a/bcos-rpc/bcos-rpc/filter/Filter.h +++ b/bcos-rpc/bcos-rpc/filter/Filter.h @@ -7,6 +7,7 @@ #include #include #include +#include namespace bcos { @@ -19,47 +20,38 @@ class Filter using Ptr = std::shared_ptr; Filter(SubscriptionType type, uint64_t id, FilterRequest::Ptr params, bool fullTx, - bcos::protocol::BlockNumber startBlockNumber, boost::asio::io_service& ioService, - int timeout, std::string_view group, std::string_view node) - : m_type(type), + bcos::protocol::BlockNumber startBlockNumber, std::string_view group) + : m_fullTx(fullTx), + m_type(type), m_id(id), - m_params(params), - m_fullTx(fullTx), + m_params(params), m_startBlockNumber(startBlockNumber), - m_deadline(ioService, boost::posix_time::seconds(timeout)), - m_group(group), - m_nodeName(node) + m_lastAccessTime(utcTime()), + m_group(group) {} - virtual ~Filter() { cancelDeadLineTimer(); } + virtual ~Filter() {} SubscriptionType type() const { return m_type; } uint64_t id() const { return m_id; } int64_t startBlockNumber() const { return m_startBlockNumber.load(); } FilterRequest::Ptr params() const { return m_params; } bool fullTx() const { return m_fullTx; } - boost::asio::deadline_timer& deadline() { return m_deadline; } + uint64_t lastAccessTime() { return m_lastAccessTime.load(); } std::string_view group() const { return m_group; } - std::string_view nodeName() const { return m_nodeName; } + void updateLastAccessTime() { m_lastAccessTime.store(utcTime()); } void setStartBlockNumber(int64_t number) { m_startBlockNumber.store(number); } - void cancelDeadLineTimer() { m_deadline.cancel(); } void setId(uint64_t id) { m_id = id; } - bool checkGroupAndNode(std::string_view group, std::string_view node) const - { - return m_group == group && m_nodeName == node; - } - private: + bool m_fullTx; SubscriptionType m_type; uint64_t m_id; FilterRequest::Ptr m_params; - bool m_fullTx; std::atomic m_startBlockNumber; - boost::asio::deadline_timer m_deadline; + std::atomic m_lastAccessTime; std::string m_group; - std::string m_nodeName; }; } // namespace rpc diff --git a/bcos-rpc/bcos-rpc/filter/FilterRequest.cpp b/bcos-rpc/bcos-rpc/filter/FilterRequest.cpp index aea5e1aa19..04816cb760 100644 --- a/bcos-rpc/bcos-rpc/filter/FilterRequest.cpp +++ b/bcos-rpc/bcos-rpc/filter/FilterRequest.cpp @@ -25,33 +25,22 @@ using namespace bcos; using namespace bcos::rpc; -static inline std::string removePrefix(const std::string& str) -{ - if ((str.compare(0, 2, "0x") == 0) || (str.compare(0, 2, "0X") == 0)) - { - return str.substr(2); - } - return str; -} - void FilterRequest::fromJson(const Json::Value& jParams, protocol::BlockNumber latest) { // default: LatestBlockNumber m_fromBlock = latest; if (jParams.isMember("fromBlock") && !jParams["fromBlock"].isNull()) { - auto [blockNumber, isLatest] = getBlockNumberByTag(latest, jParams["fromBlock"].asString()); - m_fromBlock = blockNumber; - m_fromIsLatest = isLatest; + std::tie(m_fromBlock, m_fromIsLatest) = + getBlockNumberByTag(latest, jParams["fromBlock"].asString()); } // default: LatestBlockNumber m_toBlock = latest; if (jParams.isMember("toBlock") && !jParams["toBlock"].isNull()) { - auto [blockNumber, isLatest] = getBlockNumberByTag(latest, jParams["toBlock"].asString()); - m_toBlock = blockNumber; - m_toIsLatest = isLatest; + std::tie(m_toBlock, m_toIsLatest) = + getBlockNumberByTag(latest, jParams["toBlock"].asString()); } if (jParams.isMember("address") && !jParams["address"].isNull()) @@ -61,12 +50,12 @@ void FilterRequest::fromJson(const Json::Value& jParams, protocol::BlockNumber l { for (Json::Value::ArrayIndex index = 0; index < jAddresses.size(); ++index) { - addAddress(removePrefix(jAddresses[index].asString())); + addAddress(jAddresses[index].asString()); } } else { - addAddress(removePrefix(jAddresses.asString())); + addAddress(jAddresses.asString()); } } @@ -95,12 +84,12 @@ void FilterRequest::fromJson(const Json::Value& jParams, protocol::BlockNumber l for (Json::Value::ArrayIndex innerIndex = 0; innerIndex < jIndex.size(); ++innerIndex) { - addTopic(index, removePrefix(jIndex[innerIndex].asString())); + addTopic(index, jIndex[innerIndex].asString()); } } else { // single topic, string value - addTopic(index, removePrefix(jIndex.asString())); + addTopic(index, jIndex.asString()); } } } diff --git a/bcos-rpc/bcos-rpc/filter/FilterRequest.h b/bcos-rpc/bcos-rpc/filter/FilterRequest.h index ad6094d403..c0da464259 100644 --- a/bcos-rpc/bcos-rpc/filter/FilterRequest.h +++ b/bcos-rpc/bcos-rpc/filter/FilterRequest.h @@ -66,11 +66,11 @@ class FilterRequest void setFromBlock(int64_t _fromBlock) { m_fromBlock = _fromBlock; } void setToBlock(int64_t _toBlock) { m_toBlock = _toBlock; } - void addAddress(const std::string& _address) { m_addresses.insert(_address); } + void addAddress(std::string _address) { m_addresses.insert(std::move(_address)); } void resizeTopic(size_t size) { m_topics.resize(size); } - void addTopic(std::size_t _index, const std::string& _topic) + void addTopic(std::size_t _index, std::string _topic) { - m_topics[_index].insert(_topic); + m_topics[_index].insert(std::move(_topic)); } void setBlockHash(const std::string& _hash) { m_blockHash = _hash; } void fromJson(const Json::Value& jParams, protocol::BlockNumber latest); diff --git a/bcos-rpc/bcos-rpc/filter/FilterSystem.cpp b/bcos-rpc/bcos-rpc/filter/FilterSystem.cpp new file mode 100644 index 0000000000..8ad8097dbe --- /dev/null +++ b/bcos-rpc/bcos-rpc/filter/FilterSystem.cpp @@ -0,0 +1,304 @@ +#include +#include +#include +#include +#include +#include +#include + +#define CPU_CORES std::thread::hardware_concurrency() + +using namespace bcos; +using namespace bcos::rpc; +using namespace bcos::rpc::filter; + +FilterSystem::FilterSystem(GroupManager::Ptr groupManager, const std::string& groupId, + FilterRequestFactory::Ptr factory, int threadNum, int filterTimeout, int maxBlockProcessPerReq) + : m_filterTimeout(filterTimeout * 1000), + m_maxBlockProcessPerReq(maxBlockProcessPerReq), + m_groupManager(groupManager), + m_group(groupId), + m_pool(std::make_shared("t_filter_system", threadNum)), + m_matcher(std::make_shared()), + m_factory(factory), + m_filters(CPU_CORES) +{ + // Trigger a filter cleanup operation every 3s + m_cleanUpTimer = std::make_shared(CLEANUP_FILTER_TIME, "filter_system_timer"); + m_cleanUpTimer->registerTimeoutHandler([this] { cleanUpExpiredFilters(); }); + m_cleanUpTimer->start(); +} + +uint64_t FilterSystem::insertFilter(Filter::Ptr filter) +{ + static std::mt19937_64 generator(std::random_device{}()); + uint64_t id = 0; + while (true) + { + id = generator(); + FilterMap::WriteAccessor::Ptr accessor; + if (m_filters.insert(accessor, {KeyType(filter->group(), id), filter})) + { + break; + } + } + filter->setId(id); + filter->updateLastAccessTime(); + return id; +} + +void FilterSystem::cleanUpExpiredFilters() +{ + m_cleanUpTimer->restart(); + if (m_filters.empty()) + { + return; + } + size_t traversedFiltersNum = 0; + uint64_t currentTime = utcTime(); + std::vector expiredFilters; + m_filters.forEach( + [&traversedFiltersNum, &expiredFilters, this, ¤tTime]( + FilterMap::ReadAccessor::Ptr accessor) { + const auto& filter = accessor->value(); + if (currentTime > (filter->lastAccessTime() + m_filterTimeout)) + { + expiredFilters.emplace_back(KeyType(filter->group(), filter->id())); + } + if (++traversedFiltersNum > MAX_TRAVERSE_FILTERS_COUNT) + { + return false; + } + return true; + }); + m_filters.batchRemove(expiredFilters); + FILTER_LOG(INFO) << LOG_DESC("cleanUpExpiredFilters") << LOG_KV("filters", m_filters.size()) + << LOG_KV("erasedFilters", expiredFilters.size()) + << LOG_KV("traversedFiltersNum", traversedFiltersNum); +} + +NodeService::Ptr FilterSystem::getNodeService( + std::string_view _groupID, std::string_view _command) const +{ + auto nodeService = m_groupManager->getNodeService(_groupID, ""); + if (!nodeService) + { + std::stringstream errorMsg; + errorMsg << LOG_DESC("group not exist") << LOG_KV("chain", m_groupManager->chainID()) + << LOG_KV("group", _groupID) << LOG_KV("commond", _command); + FILTER_LOG(WARNING) << errorMsg.str(); + BOOST_THROW_EXCEPTION(JsonRpcException(JsonRpcError::GroupNotExist, errorMsg.str())); + } + return nodeService; +} + +task::Task FilterSystem::newBlockFilter(std::string_view groupId) +{ + auto latestBlockNumber = getLatestBlockNumber(groupId); + auto filter = std::make_shared( + BlocksSubscription, 0, nullptr, false, latestBlockNumber + 1, groupId); + auto id = insertFilter(filter); + FILTER_LOG(TRACE) << LOG_BADGE("newBlockFilter") << LOG_KV("id", id) + << LOG_KV("startBlockNumber", latestBlockNumber); + co_return toQuantity(id); +} + +task::Task FilterSystem::newPendingTxFilter(std::string_view groupId) +{ + auto latestBlockNumber = getLatestBlockNumber(groupId); + auto filter = std::make_shared( + PendingTransactionsSubscription, 0, nullptr, false, latestBlockNumber + 1, groupId); + auto id = insertFilter(filter); + FILTER_LOG(TRACE) << LOG_BADGE("newPendingTxFilter") << LOG_KV("id", id) + << LOG_KV("startBlockNumber", latestBlockNumber); + co_return toQuantity(id); +} + +task::Task FilterSystem::newFilter(std::string_view groupId, FilterRequest::Ptr params) +{ + if (!params->checkBlockRange()) + { + FILTER_LOG(WARNING) << LOG_BADGE("newFilter") << LOG_DESC("invalid block range params") + << LOG_KV("fromBlock", params->fromBlock()) + << LOG_KV("toBlock", params->toBlock()); + BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParamsCode(), "invalid block range params")); + } + auto latestBlockNumber = getLatestBlockNumber(groupId); + auto filter = std::make_shared( + LogsSubscription, 0, params, false, latestBlockNumber + 1, groupId); + auto id = insertFilter(filter); + FILTER_LOG(TRACE) << LOG_BADGE("newFilter") << LOG_KV("id", id) + << LOG_KV("startBlockNumber", latestBlockNumber); + co_return toQuantity(id); +} + +task::Task FilterSystem::getFilterChangeImpl( + std::string_view groupId, uint64_t filterID) +{ + auto filter = getFilterByID(groupId, filterID); + if (filter == nullptr) + { + FILTER_LOG(WARNING) << LOG_BADGE("getFilterChangeImpl") << LOG_DESC("filter not found") + << LOG_KV("id", filterID); + BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParamsCode(), "filter not found")); + } + filter->updateLastAccessTime(); + FILTER_LOG(TRACE) << LOG_BADGE("getFilterChangeImpl") << LOG_KV("id", filterID) + << LOG_KV("subType", filter->type()); + + Json::Value jValue(Json::arrayValue); + switch (filter->type()) + { + case PendingTransactionsSubscription: + { + jValue = co_await getPendingTxChangeImpl(groupId, filter); + break; + } + case LogsSubscription: + { + jValue = co_await getLogChangeImpl(groupId, filter); + break; + } + case BlocksSubscription: + { + jValue = co_await getBlockChangeImpl(groupId, filter); + break; + } + default: + break; + } + co_return jValue; +} + +task::Task FilterSystem::getBlockChangeImpl( + std::string_view groupId, Filter::Ptr filter) +{ + // getLatestBlockNumber and getBlockHash use the same ledger + auto ledger = getNodeService(groupId, "getBlockChangeImpl")->ledger(); + auto latestBlockNumber = getLatestBlockNumber(*ledger); + auto startBlockNumber = filter->startBlockNumber(); + + if (latestBlockNumber < startBlockNumber) + { // Since the last query, no new blocks have been generated + co_return Json::Value(Json::arrayValue); + } + // limit the number of blocks processed + auto processBlockNum = + std::min(latestBlockNumber - startBlockNumber + 1, m_maxBlockProcessPerReq); + filter->setStartBlockNumber(startBlockNumber + processBlockNum); + + FILTER_LOG(DEBUG) << LOG_BADGE("getBlockChangeImpl") << LOG_KV("id", filter->id()) + << LOG_KV("latestBlockNumber", latestBlockNumber) + << LOG_KV("startBlockNumber", startBlockNumber) + << LOG_KV("processBlockNum", processBlockNum) + << LOG_KV("nextStartBlockNumber", startBlockNumber + processBlockNum); + Json::Value jResult(Json::arrayValue); + for (auto i = 0; i < processBlockNum; ++i) + { + auto hash = co_await ledger::getBlockHash(*ledger, startBlockNumber + i); + jResult.append(hash.hexPrefixed()); + } + co_return jResult; +} + +task::Task FilterSystem::getPendingTxChangeImpl( + std::string_view groupId, Filter::Ptr filter) +{ + // getLatestBlockNumber and getBlockData use the same ledger + auto ledger = getNodeService(groupId, "getPendingTxChangeImpl")->ledger(); + auto latestBlockNumber = getLatestBlockNumber(*ledger); + auto startBlockNumber = filter->startBlockNumber(); + if (latestBlockNumber < startBlockNumber) + { // Since the last query, no new blocks have been generated + co_return Json::Value(Json::arrayValue); + } + // limit the number of blocks processed + auto processBlockNum = + std::min(latestBlockNumber - startBlockNumber + 1, m_maxBlockProcessPerReq); + filter->setStartBlockNumber(startBlockNumber + processBlockNum); + + FILTER_LOG(DEBUG) << LOG_BADGE("getPendingTxChangeImpl") << LOG_KV("id", filter->id()) + << LOG_KV("latestBlockNumber", latestBlockNumber) + << LOG_KV("startBlockNumber", startBlockNumber) + << LOG_KV("processBlockNum", processBlockNum) + << LOG_KV("nextStartBlockNumber", startBlockNumber + processBlockNum); + + Json::Value jRes(Json::arrayValue); + for (auto i = 0; i < processBlockNum; ++i) + { + auto block = co_await ledger::getBlockData( + *ledger, i + startBlockNumber, bcos::ledger::TRANSACTIONS_HASH); + for (std::size_t index = 0; index < block->transactionsMetaDataSize(); ++index) + { + jRes.append(block->transactionHash(index).hexPrefixed()); + } + } + co_return jRes; +} + +task::Task FilterSystem::getLogChangeImpl(std::string_view groupId, Filter::Ptr filter) +{ + // getLatestBlockNumber and getLogsInternal use the same ledger + auto ledger = getNodeService(groupId, "getLogsImpl")->ledger(); + auto latestBlockNumber = getLatestBlockNumber(*ledger); + auto startBlockNumber = filter->startBlockNumber(); + auto fromBlock = filter->params()->fromBlock(); + auto toBlock = filter->params()->toBlock(); + auto fromIsLatest = filter->params()->fromIsLatest(); + auto toIsLatest = filter->params()->toIsLatest(); + + /* clang-format off */ + // ------------------------------------------------------------------------------------------- + // The values of (fromBlock, toBlock) may be: + // 1. (latest, latest) + // 2. [from, latest) + // 3. [from, to] + // ------------------------------------------------------------------------------------------- + // "end < begin" is equivalent to the following conditions: + // 1. (fromBlock, toBlock) => [from, latest) + // - from > latestBlockNumber (the block of interest has not been generated yet) + // - Since the last query, no new blocks have been generated + // 2. (fromBlock, toBlock) => [from, to] + // - from > latestBlockNumber (the block of interest has not been generated yet) + // - to < latestBlockNumber (the blocks of interest are those that have already been stored) + // - Since the last query, no new blocks have been generated + // - All blocks within the interval [from, to] have been processed + // 3. (fromBlock, toBlock) => (latest, latest) + // - Since the last query, no new blocks have been generated + // ------------------------------------------------------------------------------------------- + /* clang-format on */ + auto begin = fromIsLatest ? startBlockNumber : std::max(fromBlock, startBlockNumber); + auto end = toIsLatest ? latestBlockNumber : std::min(toBlock, latestBlockNumber); + if (end < begin) + { + co_return Json::Value(Json::arrayValue); + } + + auto params = m_factory->create(*(filter->params())); + // limit the number of blocks processed + auto processBlockNum = std::min(end - begin + 1, m_maxBlockProcessPerReq); + params->setFromBlock(begin); + params->setToBlock(begin + processBlockNum - 1); + filter->setStartBlockNumber(begin + processBlockNum); + FILTER_LOG(DEBUG) << LOG_BADGE("getLogChangeImpl") << LOG_KV("id", filter->id()) + << LOG_KV("latestBlockNumber", latestBlockNumber) + << LOG_KV("startBlockNumber", startBlockNumber) + << LOG_KV("processBlockNum", processBlockNum) + << LOG_KV("nextStartBlockNumber", begin + processBlockNum) + << LOG_KV("begin", begin) << LOG_KV("end", end) << LOG_KV("from", begin) + << LOG_KV("to", begin + processBlockNum - 1); + co_return co_await getLogsInternal(*ledger, std::move(params)); +} + +task::Task FilterSystem::getFilterLogsImpl(std::string_view groupId, uint64_t filterID) +{ + auto filter = getFilterByID(groupId, filterID); + if (filter == nullptr || filter->type() != LogsSubscription) + { + FILTER_LOG(ERROR) << LOG_BADGE("getFilterLogsImpl") << LOG_DESC("filter not found") + << LOG_KV("id", filterID); + BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParamsCode(), "filter not found")); + } + auto params = m_factory->create(*(filter->params())); + co_return co_await getLogsImpl(groupId, params, false); +} \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/filter/FilterSystem.h b/bcos-rpc/bcos-rpc/filter/FilterSystem.h new file mode 100644 index 0000000000..8f4ea71e84 --- /dev/null +++ b/bcos-rpc/bcos-rpc/filter/FilterSystem.h @@ -0,0 +1,196 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::rpc::filter +{ +struct KeyType +{ + std::string group; + uint64_t id; + + KeyType(std::string_view g, uint64_t i) : group(g), id(i) {} + + friend bool operator==(const KeyType& l, const KeyType& r) + { + return l.group == r.group && l.id == r.id; + } + + friend bool operator!=(const KeyType& l, const KeyType& r) { return !operator==(l, r); } + + template + friend Stream& operator<<(Stream& stream, const KeyType& key) + { + stream << key.group << "-" << key.id; + return stream; + } +}; +} // namespace bcos::rpc::filter + +template <> +struct std::hash +{ + size_t operator()(const bcos::rpc::filter::KeyType& key) const noexcept + { + size_t seed = std::hash{}(key.group); + boost::hash_combine(seed, std::hash{}(key.id)); + return seed; + } +}; + +namespace bcos::rpc +{ + +class FilterSystem : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + + FilterSystem(GroupManager::Ptr groupManager, const std::string& groupId, + FilterRequestFactory::Ptr factory, int threadNum, int filterTimeout, + int maxBlockProcessPerReq); + virtual ~FilterSystem() + { + m_pool->stop(); + m_cleanUpTimer->stop(); + } + +public: + // jsonrpc + task::Task newBlockFilter(std::string_view groupId); + task::Task newPendingTxFilter(std::string_view groupId); + task::Task newFilter(std::string_view groupId, FilterRequest::Ptr params); + task::Task uninstallFilter(std::string_view groupId, uint64_t filterID) + { + co_return uninstallFilterImpl(groupId, filterID); + } + task::Task getFilterChanges(std::string_view groupId, uint64_t filterID) + { + co_return co_await getFilterChangeImpl(groupId, filterID); + } + task::Task getFilterLogs(std::string_view groupId, uint64_t filterID) + { + co_return co_await getFilterLogsImpl(groupId, filterID); + } + task::Task getLogs(std::string_view groupId, FilterRequest::Ptr params) + { + co_return co_await getLogsImpl(groupId, params, true); + } + + // web3jsonrpc + task::Task newBlockFilter() { co_return co_await newBlockFilter(m_group); } + task::Task newPendingTxFilter() { co_return co_await newPendingTxFilter(m_group); } + task::Task newFilter(FilterRequest::Ptr params) + { + co_return co_await newFilter(m_group, params); + } + task::Task uninstallFilter(uint64_t filterID) + { + co_return co_await uninstallFilter(m_group, filterID); + } + task::Task getFilterChanges(uint64_t filterID) + { + co_return co_await getFilterChanges(m_group, filterID); + } + task::Task getFilterLogs(uint64_t filterID) + { + co_return co_await getFilterLogs(m_group, filterID); + } + task::Task getLogs(FilterRequest::Ptr params) + { + co_return co_await getLogs(m_group, params); + } + +public: + int64_t getLatestBlockNumber(std::string_view groupId) + { + auto ledger = getNodeService(groupId, "getCurrentBlockNumber")->ledger(); + return getLatestBlockNumber(*ledger); + } + int64_t getLatestBlockNumber() { return getLatestBlockNumber(m_group); } + + ThreadPool::Ptr pool() const { return m_pool; } + LogMatcher::Ptr matcher() const { return m_matcher; } + FilterRequest::Ptr createFilterRequest() { return m_factory->create(); } + NodeService::Ptr getNodeService(std::string_view _groupID, std::string_view _command) const; + +protected: + bool uninstallFilterImpl(std::string_view groupId, uint64_t filterID) + { + return m_filters.remove(filter::KeyType(groupId, filterID)) != nullptr; + } + task::Task getFilterChangeImpl(std::string_view groupId, uint64_t filterID); + task::Task getBlockChangeImpl(std::string_view groupId, Filter::Ptr filter); + task::Task getPendingTxChangeImpl(std::string_view groupId, Filter::Ptr filter); + task::Task getLogChangeImpl(std::string_view groupId, Filter::Ptr filter); + task::Task getFilterLogsImpl(std::string_view groupId, uint64_t filterID); + task::Task getLogsImpl( + std::string_view groupId, FilterRequest::Ptr params, bool needCheckRange) + { + co_return Json::Value(Json::arrayValue); + } + task::Task getLogsInPool( + bcos::ledger::LedgerInterface::Ptr ledger, FilterRequest::Ptr params) + { + co_return Json::Value(Json::arrayValue); + } + task::Task getLogsInternal( + bcos::ledger::LedgerInterface& ledger, FilterRequest::Ptr params) + { + co_return Json::Value(Json::arrayValue); + } + +protected: + virtual int32_t InvalidParamsCode() = 0; + uint64_t insertFilter(Filter::Ptr filter); + void cleanUpExpiredFilters(); + + Filter::Ptr getFilterByID(std::string_view groupId, uint64_t id) + { + FilterMap::ReadAccessor::Ptr accessor; + if (!m_filters.find(accessor, filter::KeyType(groupId, id))) + { + return nullptr; + } + return accessor->value(); + } + static int64_t getLatestBlockNumber(bcos::ledger::LedgerInterface& _ledger) + { + int64_t latest = 0; + task::wait([](bcos::ledger::LedgerInterface& ledger, int64_t& ret) -> task::Task { + ret = co_await ledger::getCurrentBlockNumber(ledger); + }(_ledger, latest)); + return latest; + } + +protected: + using FilterMap = BucketMap>; + + uint64_t m_filterTimeout = FILTER_DEFAULT_EXPIRATION_TIME; + int64_t m_maxBlockProcessPerReq = 10; + GroupManager::Ptr m_groupManager; + std::string m_group; + ThreadPool::Ptr m_pool; + LogMatcher::Ptr m_matcher; + FilterRequestFactory::Ptr m_factory; + FilterMap m_filters; + // timer to clear up the expired filter in-period + std::shared_ptr m_cleanUpTimer; +}; + +} // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/filter/LogMatcher.cpp b/bcos-rpc/bcos-rpc/filter/LogMatcher.cpp index 70b53576db..2b3d7ff45e 100644 --- a/bcos-rpc/bcos-rpc/filter/LogMatcher.cpp +++ b/bcos-rpc/bcos-rpc/filter/LogMatcher.cpp @@ -12,15 +12,15 @@ uint32_t LogMatcher::matches( for (std::size_t index = 0; index < _block->transactionsMetaDataSize(); index++) { count += matches(_params, _block->blockHeaderConst()->hash(), _block->receipt(index), - _block->transactionMetaData(index), index, _result); + _block->transactionHash(index), index, _result); } return count; } uint32_t LogMatcher::matches(FilterRequest::ConstPtr _params, bcos::crypto::HashType&& _blockHash, - bcos::protocol::TransactionReceipt::ConstPtr&& _receipt, - bcos::protocol::TransactionMetaData::ConstPtr&& _tx, std::size_t _txIndex, Json::Value& _result) + bcos::protocol::TransactionReceipt::ConstPtr&& _receipt, bcos::crypto::HashType&& _txHash, + std::size_t _txIndex, Json::Value& _result) { uint32_t count = 0; auto blockNumber = _receipt->blockNumber(); @@ -38,7 +38,7 @@ uint32_t LogMatcher::matches(FilterRequest::ConstPtr _params, bcos::crypto::Hash log["blockNumber"] = toQuantity(blockNumber); log["blockHash"] = _blockHash.hexPrefixed(); log["transactionIndex"] = toQuantity(_txIndex); - log["transactionHash"] = _tx->hash().hexPrefixed(); + log["transactionHash"] = _txHash.hexPrefixed(); log["removed"] = false; log["address"] = "0x" + std::string(logEntry.address()); Json::Value jTopics(Json::arrayValue); @@ -63,7 +63,7 @@ bool LogMatcher::matches(FilterRequest::ConstPtr _params, const bcos::protocol:: << LOG_KV("logEntry topics", _logEntry.topics().size()); // An empty address array matches all values otherwise log.address must be in addresses - if (!addresses.empty() && !addresses.count(std::string(_logEntry.address()))) + if (!addresses.empty() && !addresses.count("0x" + std::string(_logEntry.address()))) { return false; } @@ -80,7 +80,7 @@ bool LogMatcher::matches(FilterRequest::ConstPtr _params, const bcos::protocol:: { continue; } - if (!sub.contains(logTopics[i].hex())) + if (!sub.contains(logTopics[i].hexPrefixed())) { return false; } diff --git a/bcos-rpc/bcos-rpc/filter/LogMatcher.h b/bcos-rpc/bcos-rpc/filter/LogMatcher.h index 378ef1417b..21f9d5e2df 100644 --- a/bcos-rpc/bcos-rpc/filter/LogMatcher.h +++ b/bcos-rpc/bcos-rpc/filter/LogMatcher.h @@ -18,15 +18,14 @@ class LogMatcher ~LogMatcher() {} public: - bool matches( - FilterRequest::ConstPtr _params, const bcos::protocol::LogEntry& _logEntry); + bool matches(FilterRequest::ConstPtr _params, const bcos::protocol::LogEntry& _logEntry); uint32_t matches(FilterRequest::ConstPtr _params, bcos::crypto::HashType&& _blockHash, - bcos::protocol::TransactionReceipt::ConstPtr&& _receipt, - bcos::protocol::TransactionMetaData::ConstPtr&& _tx, std::size_t _txIndex, Json::Value& _result); + bcos::protocol::TransactionReceipt::ConstPtr&& _receipt, bcos::crypto::HashType&& _txHash, + std::size_t _txIndex, Json::Value& _result); - uint32_t matches( - FilterRequest::ConstPtr _params, bcos::protocol::Block::ConstPtr _block, Json::Value& _result); + uint32_t matches(FilterRequest::ConstPtr _params, bcos::protocol::Block::ConstPtr _block, + Json::Value& _result); }; } // namespace rpc diff --git a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp index 80dabfc226..9fa07c9402 100644 --- a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp +++ b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp @@ -43,7 +43,6 @@ #include #include -#include #include using namespace bcos; @@ -465,7 +464,7 @@ BOOST_AUTO_TEST_CASE(logMatcherTest) protocol::LogEntry log2(address1, {C, D}, bytes()); auto params1 = std::make_shared(); auto params2 = std::make_shared(); - params2->addAddress(*toHexString(address1)); + params2->addAddress(toHexStringWithPrefix(address1)); BOOST_CHECK(matcher.matches(params1, log1)); BOOST_CHECK(matcher.matches(params1, log2)); BOOST_CHECK(!matcher.matches(params2, log1)); @@ -475,7 +474,7 @@ BOOST_AUTO_TEST_CASE(logMatcherTest) { auto params = std::make_shared(); params->resizeTopic(1); - params->addTopic(0, *toHexString(A)); + params->addTopic(0, toHexStringWithPrefix(A)); protocol::LogEntry log; // [A] log = protocol::LogEntry(address1, {A}, bytes()); @@ -494,7 +493,7 @@ BOOST_AUTO_TEST_CASE(logMatcherTest) { auto params = std::make_shared(); params->resizeTopic(2); - params->addTopic(1, *toHexString(B)); + params->addTopic(1, toHexStringWithPrefix(B)); protocol::LogEntry log; // matched // [A, B] @@ -539,8 +538,8 @@ BOOST_AUTO_TEST_CASE(logMatcherTest) { auto params = std::make_shared(); params->resizeTopic(2); - params->addTopic(0, *toHexString(A)); - params->addTopic(1, *toHexString(B)); + params->addTopic(0, toHexStringWithPrefix(A)); + params->addTopic(1, toHexStringWithPrefix(B)); protocol::LogEntry log; // matched @@ -579,10 +578,10 @@ BOOST_AUTO_TEST_CASE(logMatcherTest) { auto params = std::make_shared(); params->resizeTopic(2); - params->addTopic(0, *toHexString(A)); - params->addTopic(0, *toHexString(B)); - params->addTopic(1, *toHexString(C)); - params->addTopic(1, *toHexString(D)); + params->addTopic(0, toHexStringWithPrefix(A)); + params->addTopic(0, toHexStringWithPrefix(B)); + params->addTopic(1, toHexStringWithPrefix(C)); + params->addTopic(1, toHexStringWithPrefix(D)); protocol::LogEntry log; // matched From f1f92bb37b9f9b81d6a247fd209360d811dc4168 Mon Sep 17 00:00:00 2001 From: jimmyshi <417711026@qq.com> Date: Thu, 9 May 2024 17:40:10 +0800 Subject: [PATCH 18/39] (execute): bugfix_staticcall_noaddr_return & bugfix_support_transfer_receive_fallback (#4443) Co-authored-by: wenlinli <1574249665@qq.com> --- .../src/executive/TransactionExecutive.cpp | 23 +++++++++++++++++-- .../extension/AccountPrecompiled.cpp | 23 ++++++++++++++++++- .../bcos-framework/ledger/Features.h | 6 ++++- .../unittests/interfaces/FeaturesTest.cpp | 2 ++ 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/bcos-executor/src/executive/TransactionExecutive.cpp b/bcos-executor/src/executive/TransactionExecutive.cpp index 7ab05dc09e..f0c5495d14 100644 --- a/bcos-executor/src/executive/TransactionExecutive.cpp +++ b/bcos-executor/src/executive/TransactionExecutive.cpp @@ -239,6 +239,18 @@ CallParameters::UniquePtr TransactionExecutive::execute(CallParameters::UniquePt callParameters->value > 0) { bool onlyTransfer = callParameters->data.empty(); + if (m_blockContext.features().get( + ledger::Features::Flag::bugfix_support_transfer_receive_fallback)) + { + // Note: To support receive fallback. + // Transfer from EOA or contract tx's data is empty. But we still need to executive as + // an normal transaction. + // If "to" is contract, go to contract's receive() fallback. + // If "to" is EOA, go to AccountPrecompiled receive() logic. + onlyTransfer = false; + } + + bool transferFromEVM = callParameters->seq != 0; int64_t requiredGas = transferFromEVM ? 0 : BALANCE_TRANSFER_GAS; auto currentContextAddress = callParameters->receiveAddress; @@ -1142,11 +1154,14 @@ CallParameters::UniquePtr TransactionExecutive::go( ) { // Note: to be the same as eth - // Just fix DMC: // if bugfix_call_noaddr_return is not set, callResult->evmStatus is still // default to EVMC_SUCCESS and serial mode is execute same as eth, but DMC is // using callResult->status, so we need to compat with DMC here - + if (m_blockContext.features().get( + ledger::Features::Flag::bugfix_staticcall_noaddr_return)) + { + callResult->data = bytes(); + } callResult->type = CallParameters::FINISHED; callResult->evmStatus = EVMC_SUCCESS; callResult->status = (int32_t)TransactionStatus::None; @@ -1520,6 +1535,10 @@ CallParameters::UniquePtr TransactionExecutive::parseEVMCResult( "Execution tried to execute an operation which is restricted in static mode.", *callResults); } + if (m_blockContext.features().get(ledger::Features::Flag::bugfix_staticcall_noaddr_return)) + { + callResults->data = bytes(); + } callResults->status = (int32_t)TransactionStatus::Unknown; revert(); break; diff --git a/bcos-executor/src/precompiled/extension/AccountPrecompiled.cpp b/bcos-executor/src/precompiled/extension/AccountPrecompiled.cpp index e9bbb1e2e2..f1d1e48c00 100644 --- a/bcos-executor/src/precompiled/extension/AccountPrecompiled.cpp +++ b/bcos-executor/src/precompiled/extension/AccountPrecompiled.cpp @@ -33,6 +33,7 @@ const char* const AM_METHOD_GET_ACCOUNT_STATUS = "getAccountStatus()"; const char* const AM_METHOD_GET_ACCOUNT_BALANCE = "getAccountBalance()"; const char* const AM_METHOD_ADD_ACCOUNT_BALANCE = "addAccountBalance(uint256)"; const char* const AM_METHOD_SUB_ACCOUNT_BALANCE = "subAccountBalance(uint256)"; +const uint32_t AM_METHOD_RECEIVE_FALLBACK_SELECTOR = 1; AccountPrecompiled::AccountPrecompiled(crypto::Hash::Ptr hashImpl) : Precompiled(hashImpl) @@ -62,7 +63,18 @@ std::shared_ptr AccountPrecompiled::call( auto accountTableName = dynamicParams.at(0); // get user call actual params auto originParam = ref(param); - uint32_t func = getParamFunc(originParam); + uint32_t func; + if (originParam.size() == 0 && + blockContext.features().get( + ledger::Features::Flag::bugfix_support_transfer_receive_fallback)) + { + // Transfer to EOA operation, call receive() function + func = AM_METHOD_RECEIVE_FALLBACK_SELECTOR; + } + else + { + func = getParamFunc(originParam); + } bytesConstRef data = getParamData(originParam); auto table = _executive->storage().openTable(accountTableName); @@ -86,6 +98,15 @@ std::shared_ptr AccountPrecompiled::call( { subAccountBalance(accountTableName, _executive, data, _callParameters); } + else if (func == AM_METHOD_RECEIVE_FALLBACK_SELECTOR) + { + // Transfer to EOA operation + // receive() fallback logic + // Just return _callParameters, do noting + PRECOMPILED_LOG(TRACE) << LOG_BADGE("AccountPrecompiled") + << LOG_DESC("call receive() function. do nothing") + << LOG_KV("func", func); + } else { PRECOMPILED_LOG(INFO) << LOG_BADGE("AccountPrecompiled") diff --git a/bcos-framework/bcos-framework/ledger/Features.h b/bcos-framework/bcos-framework/ledger/Features.h index 11ff976a01..ac9347dfea 100644 --- a/bcos-framework/bcos-framework/ledger/Features.h +++ b/bcos-framework/bcos-framework/ledger/Features.h @@ -43,6 +43,8 @@ class Features bugfix_eip55_addr, bugfix_eoa_as_contract, bugfix_dmc_deploy_gas_used, + bugfix_staticcall_noaddr_return, + bugfix_support_transfer_receive_fallback, feature_dmc2serial, feature_sharding, feature_rpbft, @@ -156,7 +158,9 @@ class Features Flag::bugfix_sharding_call_in_child_executive, Flag::bugfix_internal_create_permission_denied}}, {protocol::BlockVersion::V3_7_3_VERSION, - {Flag::bugfix_eoa_as_contract, Flag::bugfix_dmc_deploy_gas_used}}}); + {Flag::bugfix_eoa_as_contract, Flag::bugfix_dmc_deploy_gas_used, + Flag::bugfix_staticcall_noaddr_return, + Flag::bugfix_support_transfer_receive_fallback}}}); for (const auto& upgradeFeatures : upgradeRoadmap) { if (((to < protocol::BlockVersion::V3_2_7_VERSION) && (to >= upgradeFeatures.to)) || diff --git a/bcos-framework/test/unittests/interfaces/FeaturesTest.cpp b/bcos-framework/test/unittests/interfaces/FeaturesTest.cpp index e6c9804209..781ac95e3e 100644 --- a/bcos-framework/test/unittests/interfaces/FeaturesTest.cpp +++ b/bcos-framework/test/unittests/interfaces/FeaturesTest.cpp @@ -143,6 +143,8 @@ BOOST_AUTO_TEST_CASE(feature) "bugfix_eip55_addr", "bugfix_eoa_as_contract", "bugfix_dmc_deploy_gas_used", + "bugfix_staticcall_noaddr_return", + "bugfix_support_transfer_receive_fallback", "feature_dmc2serial", "feature_sharding", "feature_rpbft", From a9e9448033aaeb1b0b86f03c411fd624f5793f63 Mon Sep 17 00:00:00 2001 From: jimmyshi <417711026@qq.com> Date: Fri, 10 May 2024 11:32:00 +0800 Subject: [PATCH 19/39] sync code from 3.8.0: bugfix_evm_exception_gas_used & bugfix_set_row_with_dirty_flag (#4445) Co-authored-by: MO NAN <651932351@qq.com> --- .../src/executive/TransactionExecutive.cpp | 40 +++ .../executor/ShardingTransactionExecutor.cpp | 6 +- .../src/executor/TransactionExecutor.cpp | 48 ++- .../src/executor/TransactionExecutor.h | 5 +- .../unittest/libexecutor/TestEVMExecutor.cpp | 2 +- .../libprecompiled/ContractShardUtilsTest.cpp | 6 +- .../SystemConfigPrecompileTest.cpp | 4 +- .../unittest/mock/MockTransactionalStorage.h | 2 +- .../bcos-framework/ledger/Features.h | 7 +- .../unittests/interfaces/FeaturesTest.cpp | 2 + bcos-ledger/src/libledger/LedgerImpl.h | 7 +- .../test/unittests/ledger/LedgerTest.cpp | 12 +- bcos-pbft/test/unittests/pbft/PBFTFixture.h | 15 +- .../test/unittests/config/RPBFTConfigTest.cpp | 3 +- bcos-scheduler/src/BlockExecutive.cpp | 323 +++++++++--------- bcos-scheduler/src/ShardingBlockExecutive.cpp | 2 +- bcos-storage/test/unittest/StorageTest.cpp | 2 +- .../test/unittest/TestRocksDBStorage.cpp | 9 +- .../test/unittest/TestTiKVStorage.cpp | 52 ++- bcos-table/src/CacheStorageFactory.cpp | 2 +- bcos-table/src/StateStorage.h | 14 +- bcos-table/src/StateStorageFactory.h | 9 +- bcos-table/test/unittests/libtable/Table.cpp | 8 +- .../test/unittests/libtable/TablePerf.cpp | 4 +- .../unittests/libtable/TestKeyPageStorage.cpp | 74 ++-- .../unittests/libtable/TestStateStorage.cpp | 40 +-- tests/perf/benchmark.cpp | 10 +- 27 files changed, 394 insertions(+), 314 deletions(-) diff --git a/bcos-executor/src/executive/TransactionExecutive.cpp b/bcos-executor/src/executive/TransactionExecutive.cpp index f0c5495d14..3963236936 100644 --- a/bcos-executor/src/executive/TransactionExecutive.cpp +++ b/bcos-executor/src/executive/TransactionExecutive.cpp @@ -1466,6 +1466,10 @@ CallParameters::UniquePtr TransactionExecutive::parseEVMCResult( << LOG_KV("to", callResults->receiveAddress); callResults->status = (int32_t)TransactionStatus::BadInstruction; revert(); + if (m_blockContext.features().get(ledger::Features::Flag::bugfix_evm_exception_gas_used)) + { + callResults->gas = _result.gasLeft(); + } if (versionCompareTo(m_blockContext.blockVersion(), BlockVersion::V3_1_VERSION) >= 0) { writeErrInfoToOutput("Execution invalid/undefined opcode.", *callResults); @@ -1484,6 +1488,10 @@ CallParameters::UniquePtr TransactionExecutive::parseEVMCResult( "Execution has violated the jump destination restrictions.", *callResults); } revert(); + if (m_blockContext.features().get(ledger::Features::Flag::bugfix_evm_exception_gas_used)) + { + callResults->gas = _result.gasLeft(); + } break; } case EVMC_STACK_OVERFLOW: @@ -1497,6 +1505,10 @@ CallParameters::UniquePtr TransactionExecutive::parseEVMCResult( writeErrInfoToOutput("Execution stack overflow.", *callResults); } revert(); + if (m_blockContext.features().get(ledger::Features::Flag::bugfix_evm_exception_gas_used)) + { + callResults->gas = _result.gasLeft(); + } break; } case EVMC_STACK_UNDERFLOW: @@ -1509,6 +1521,10 @@ CallParameters::UniquePtr TransactionExecutive::parseEVMCResult( writeErrInfoToOutput("Execution needs more items on EVM stack.", *callResults); } revert(); + if (m_blockContext.features().get(ledger::Features::Flag::bugfix_evm_exception_gas_used)) + { + callResults->gas = _result.gasLeft(); + } break; } case EVMC_INVALID_MEMORY_ACCESS: @@ -1522,6 +1538,10 @@ CallParameters::UniquePtr TransactionExecutive::parseEVMCResult( writeErrInfoToOutput("Execution tried to read outside memory bounds.", *callResults); } revert(); + if (m_blockContext.features().get(ledger::Features::Flag::bugfix_evm_exception_gas_used)) + { + callResults->gas = _result.gasLeft(); + } break; } case EVMC_STATIC_MODE_VIOLATION: @@ -1541,6 +1561,10 @@ CallParameters::UniquePtr TransactionExecutive::parseEVMCResult( } callResults->status = (int32_t)TransactionStatus::Unknown; revert(); + if (m_blockContext.features().get(ledger::Features::Flag::bugfix_evm_exception_gas_used)) + { + callResults->gas = _result.gasLeft(); + } break; } case EVMC_CONTRACT_VALIDATION_FAILURE: @@ -1554,6 +1578,10 @@ CallParameters::UniquePtr TransactionExecutive::parseEVMCResult( writeErrInfoToOutput("Contract validation has failed.", *callResults); } revert(); + if (m_blockContext.features().get(ledger::Features::Flag::bugfix_evm_exception_gas_used)) + { + callResults->gas = _result.gasLeft(); + } break; } case EVMC_ARGUMENT_OUT_OF_RANGE: @@ -1569,6 +1597,10 @@ CallParameters::UniquePtr TransactionExecutive::parseEVMCResult( *callResults); } revert(); + if (m_blockContext.features().get(ledger::Features::Flag::bugfix_evm_exception_gas_used)) + { + callResults->gas = _result.gasLeft(); + } break; } case EVMC_WASM_TRAP: @@ -1584,6 +1616,10 @@ CallParameters::UniquePtr TransactionExecutive::parseEVMCResult( writeErrInfoToOutput("A WebAssembly trap has been hit during execution.", *callResults); } revert(); + if (m_blockContext.features().get(ledger::Features::Flag::bugfix_evm_exception_gas_used)) + { + callResults->gas = _result.gasLeft(); + } break; } case EVMC_INTERNAL_ERROR: @@ -1593,6 +1629,10 @@ CallParameters::UniquePtr TransactionExecutive::parseEVMCResult( << LOG_KV("to", callResults->receiveAddress) << LOG_KV("status", _result.status()); revert(); + if (m_blockContext.features().get(ledger::Features::Flag::bugfix_evm_exception_gas_used)) + { + callResults->gas = _result.gasLeft(); + } if (_result.status() <= EVMC_INTERNAL_ERROR) { BOOST_THROW_EXCEPTION(InternalVMError{} << errinfo_evmcStatusCode(_result.status())); diff --git a/bcos-executor/src/executor/ShardingTransactionExecutor.cpp b/bcos-executor/src/executor/ShardingTransactionExecutor.cpp index 38a3f62435..f45d4dd459 100644 --- a/bcos-executor/src/executor/ShardingTransactionExecutor.cpp +++ b/bcos-executor/src/executor/ShardingTransactionExecutor.cpp @@ -166,11 +166,13 @@ BlockContext::Ptr ShardingTransactionExecutor::createTmpBlockContext( if (m_cachedStorage) { - stateStorage = createStateStorage(m_cachedStorage, true); + stateStorage = createStateStorage(m_cachedStorage, true, + m_blockContext->features().get(ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); } else { - stateStorage = createStateStorage(m_backendStorage, true); + stateStorage = createStateStorage(m_backendStorage, true, + m_blockContext->features().get(ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); } return createBlockContext(currentHeader, stateStorage); diff --git a/bcos-executor/src/executor/TransactionExecutor.cpp b/bcos-executor/src/executor/TransactionExecutor.cpp index 9e7a1899ae..8c499e403e 100644 --- a/bcos-executor/src/executor/TransactionExecutor.cpp +++ b/bcos-executor/src/executor/TransactionExecutor.cpp @@ -455,13 +455,17 @@ void TransactionExecutor::nextBlockHeader(int64_t schedulerTermId, bcos::storage::StateStorageInterface::Ptr stateStorage; if (m_stateStorages.empty()) { + auto withDirtyFlag = + m_blockContext ? m_blockContext->features().get( + ledger::Features::Flag::bugfix_set_row_with_dirty_flag) : + false; if (m_cachedStorage) { - stateStorage = createStateStorage(m_cachedStorage); + stateStorage = createStateStorage(m_cachedStorage, false, withDirtyFlag); } else { - stateStorage = createStateStorage(m_backendStorage); + stateStorage = createStateStorage(m_backendStorage, false, withDirtyFlag); } // check storage block Number @@ -505,7 +509,9 @@ void TransactionExecutor::nextBlockHeader(int64_t schedulerTermId, } prev.storage->setReadOnly(true); - stateStorage = createStateStorage(prev.storage); + stateStorage = createStateStorage(prev.storage, false, + m_blockContext->features().get( + ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); } if (m_blockContext) @@ -628,7 +634,8 @@ void TransactionExecutor::dmcCall(bcos::protocol::ExecutionMessage::UniquePtr in } // Create a temp storage - auto storage = createStateStorage(std::move(prev), true); + auto storage = createStateStorage(std::move(prev), true, + m_blockContext->features().get(ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); // Create a temp block context blockContext = createBlockContextForCall( @@ -797,7 +804,8 @@ void TransactionExecutor::call(bcos::protocol::ExecutionMessage::UniquePtr input } // Create a temp storage - auto storage = createStateStorage(std::move(prev), true); + auto storage = createStateStorage(std::move(prev), true, + m_blockContext->features().get(ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); // Create a temp block context blockContext = createBlockContextForCall( @@ -1982,7 +1990,9 @@ void TransactionExecutor::getCode( std::unique_lock lock(m_stateStoragesMutex); if (!m_stateStorages.empty()) { - stateStorage = createStateStorage(m_stateStorages.back().storage, true); + stateStorage = createStateStorage(m_stateStorages.back().storage, true, + m_blockContext->features().get( + ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); } } // create temp state storage @@ -1990,11 +2000,15 @@ void TransactionExecutor::getCode( { if (m_cachedStorage) { - stateStorage = createStateStorage(m_cachedStorage, true); + stateStorage = createStateStorage(m_cachedStorage, true, + m_blockContext->features().get( + ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); } else { - stateStorage = createStateStorage(m_backendStorage, true); + stateStorage = createStateStorage(m_backendStorage, true, + m_blockContext->features().get( + ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); } } @@ -2110,7 +2124,9 @@ void TransactionExecutor::getABI( std::unique_lock lock(m_stateStoragesMutex); if (!m_stateStorages.empty()) { - stateStorage = createStateStorage(m_stateStorages.back().storage, true); + stateStorage = createStateStorage(m_stateStorages.back().storage, true, + m_blockContext->features().get( + ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); } } // create temp state storage @@ -2118,11 +2134,15 @@ void TransactionExecutor::getABI( { if (m_cachedStorage) { - stateStorage = createStateStorage(m_cachedStorage, true); + stateStorage = createStateStorage(m_cachedStorage, true, + m_blockContext->features().get( + ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); } else { - stateStorage = createStateStorage(m_backendStorage, true); + stateStorage = createStateStorage(m_backendStorage, true, + m_blockContext->features().get( + ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); } } @@ -2818,10 +2838,10 @@ void TransactionExecutor::executeTransactionsWithCriticals( } bcos::storage::StateStorageInterface::Ptr TransactionExecutor::createStateStorage( - bcos::storage::StorageInterface::Ptr storage, bool ignoreNotExist) + bcos::storage::StorageInterface::Ptr storage, bool ignoreNotExist, bool setRowWithDirtyFlag) { - auto stateStorage = m_stateStorageFactory->createStateStorage( - std::move(storage), m_blockVersion, ignoreNotExist, m_keyPageIgnoreTables); + auto stateStorage = m_stateStorageFactory->createStateStorage(std::move(storage), + m_blockVersion, setRowWithDirtyFlag, ignoreNotExist, m_keyPageIgnoreTables); return stateStorage; } diff --git a/bcos-executor/src/executor/TransactionExecutor.h b/bcos-executor/src/executor/TransactionExecutor.h index 25bb568f47..2872ff455d 100644 --- a/bcos-executor/src/executor/TransactionExecutor.h +++ b/bcos-executor/src/executor/TransactionExecutor.h @@ -127,7 +127,7 @@ class TransactionExecutor : public ParallelTransactionExecutorInterface, void preExecuteTransactions(int64_t schedulerTermId, const bcos::protocol::BlockHeader::ConstPtr& blockHeader, std::string contractAddress, gsl::span inputs, - std::function callback) override{ + std::function callback) override { // do nothing }; @@ -249,7 +249,8 @@ class TransactionExecutor : public ParallelTransactionExecutorInterface, bcos::storage::StateStorageInterface::Ptr createStateStorage( - bcos::storage::StorageInterface::Ptr storage, bool ignoreNotExist = false); + bcos::storage::StorageInterface::Ptr storage, bool ignoreNotExist, + bool setRowWithDirtyFlag); protocol::BlockNumber getBlockNumberInStorage(); protocol::BlockHeader::Ptr getBlockHeaderInStorage(protocol::BlockNumber number); diff --git a/bcos-executor/test/unittest/libexecutor/TestEVMExecutor.cpp b/bcos-executor/test/unittest/libexecutor/TestEVMExecutor.cpp index cf5db000d0..b1943e0a2e 100644 --- a/bcos-executor/test/unittest/libexecutor/TestEVMExecutor.cpp +++ b/bcos-executor/test/unittest/libexecutor/TestEVMExecutor.cpp @@ -81,7 +81,7 @@ struct TransactionExecutorFixture ledger = std::make_shared(); auto executionResultFactory = std::make_shared(); - auto lruStorage = std::make_shared(backend); + auto lruStorage = std::make_shared(backend, false); auto stateStorageFactory = std::make_shared(0); executor = bcos::executor::TransactionExecutorFactory::build(ledger, txpool, lruStorage, backend, executionResultFactory, stateStorageFactory, hashImpl, false, false); diff --git a/bcos-executor/test/unittest/libprecompiled/ContractShardUtilsTest.cpp b/bcos-executor/test/unittest/libprecompiled/ContractShardUtilsTest.cpp index 8f4ee3d731..aa470b912c 100644 --- a/bcos-executor/test/unittest/libprecompiled/ContractShardUtilsTest.cpp +++ b/bcos-executor/test/unittest/libprecompiled/ContractShardUtilsTest.cpp @@ -42,9 +42,9 @@ class ContractShardUtilsTestFixture : public PrecompiledFixture ~ContractShardUtilsTestFixture() override = default; - StorageWrapper storage = - StorageWrapper(std::make_shared(PrecompiledFixture::storage), - std::make_shared()); + StorageWrapper storage = StorageWrapper( + std::make_shared(PrecompiledFixture::storage, false), + std::make_shared()); }; BOOST_FIXTURE_TEST_SUITE(ContractShardUtilsTest, ContractShardUtilsTestFixture) diff --git a/bcos-executor/test/unittest/libprecompiled/SystemConfigPrecompileTest.cpp b/bcos-executor/test/unittest/libprecompiled/SystemConfigPrecompileTest.cpp index c86b9c3931..5da04be3e1 100644 --- a/bcos-executor/test/unittest/libprecompiled/SystemConfigPrecompileTest.cpp +++ b/bcos-executor/test/unittest/libprecompiled/SystemConfigPrecompileTest.cpp @@ -44,8 +44,8 @@ struct SystemConfigPrecompiledFixture : public bcos::test::PrecompiledFixture std::shared_ptr ledgerCache = std::make_shared(std::make_shared()); std::shared_ptr gasInjector; - std::shared_ptr backendStorage = std::make_shared(nullptr); - std::shared_ptr stateStorage = std::make_shared(backendStorage); + std::shared_ptr backendStorage = std::make_shared(nullptr, false); + std::shared_ptr stateStorage = std::make_shared(backendStorage, false); std::shared_ptr blockContext = std::make_shared(stateStorage, ledgerCache, hashImpl, 0, h256(), utcTime(), static_cast(protocol::BlockVersion::V3_1_VERSION), FiscoBcosSchedule, false, diff --git a/bcos-executor/test/unittest/mock/MockTransactionalStorage.h b/bcos-executor/test/unittest/mock/MockTransactionalStorage.h index 1206f8cf8a..e5f21a16f2 100644 --- a/bcos-executor/test/unittest/mock/MockTransactionalStorage.h +++ b/bcos-executor/test/unittest/mock/MockTransactionalStorage.h @@ -16,7 +16,7 @@ class MockTransactionalStorage : public bcos::storage::TransactionalStorageInter public: MockTransactionalStorage(bcos::crypto::Hash::Ptr hashImpl) : m_hashImpl(std::move(hashImpl)) { - m_inner = std::make_shared(nullptr); + m_inner = std::make_shared(nullptr, false); m_inner->setEnableTraverse(true); } diff --git a/bcos-framework/bcos-framework/ledger/Features.h b/bcos-framework/bcos-framework/ledger/Features.h index ac9347dfea..65a9ef6a54 100644 --- a/bcos-framework/bcos-framework/ledger/Features.h +++ b/bcos-framework/bcos-framework/ledger/Features.h @@ -45,6 +45,8 @@ class Features bugfix_dmc_deploy_gas_used, bugfix_staticcall_noaddr_return, bugfix_support_transfer_receive_fallback, + bugfix_evm_exception_gas_used, + bugfix_set_row_with_dirty_flag, feature_dmc2serial, feature_sharding, feature_rpbft, @@ -160,7 +162,10 @@ class Features {protocol::BlockVersion::V3_7_3_VERSION, {Flag::bugfix_eoa_as_contract, Flag::bugfix_dmc_deploy_gas_used, Flag::bugfix_staticcall_noaddr_return, - Flag::bugfix_support_transfer_receive_fallback}}}); + Flag::bugfix_support_transfer_receive_fallback, + Flag::bugfix_evm_exception_gas_used, + Flag::bugfix_set_row_with_dirty_flag}}}); + for (const auto& upgradeFeatures : upgradeRoadmap) { if (((to < protocol::BlockVersion::V3_2_7_VERSION) && (to >= upgradeFeatures.to)) || diff --git a/bcos-framework/test/unittests/interfaces/FeaturesTest.cpp b/bcos-framework/test/unittests/interfaces/FeaturesTest.cpp index 781ac95e3e..94e58756ad 100644 --- a/bcos-framework/test/unittests/interfaces/FeaturesTest.cpp +++ b/bcos-framework/test/unittests/interfaces/FeaturesTest.cpp @@ -145,6 +145,8 @@ BOOST_AUTO_TEST_CASE(feature) "bugfix_dmc_deploy_gas_used", "bugfix_staticcall_noaddr_return", "bugfix_support_transfer_receive_fallback", + "bugfix_evm_exception_gas_used", + "bugfix_set_row_with_dirty_flag", "feature_dmc2serial", "feature_sharding", "feature_rpbft", diff --git a/bcos-ledger/src/libledger/LedgerImpl.h b/bcos-ledger/src/libledger/LedgerImpl.h index 04d2a3003c..9af6709ecb 100644 --- a/bcos-ledger/src/libledger/LedgerImpl.h +++ b/bcos-ledger/src/libledger/LedgerImpl.h @@ -1,6 +1,7 @@ #pragma once #include "Ledger.h" +#include "bcos-framework/ledger/Ledger.h" #include "bcos-task/Task.h" #include #include @@ -201,8 +202,12 @@ class LedgerImpl : public bcos::concepts::ledger::LedgerBase>>( storage::IGNORED_ARRAY_310.begin(), storage::IGNORED_ARRAY_310.end()); + + // 此处为只读操作,不可能写,因此无需关注setRowWithDirtyFlag + // This is a read-only operation and it is impossible to write, so there is no need to pay + // attention to setRowWithDirtyFlag auto stateStorage = stateStorageFactory->createStateStorage( - m_backupStorage, m_compatibilityVersion, true, keyPageIgnoreTables); + m_backupStorage, m_compatibilityVersion, false, true, keyPageIgnoreTables); // try to get codeHash auto codeHashEntry = stateStorage->getRow(contractTableName, "codeHash"); diff --git a/bcos-ledger/test/unittests/ledger/LedgerTest.cpp b/bcos-ledger/test/unittests/ledger/LedgerTest.cpp index fa4a2f3be6..194bcebac3 100644 --- a/bcos-ledger/test/unittests/ledger/LedgerTest.cpp +++ b/bcos-ledger/test/unittests/ledger/LedgerTest.cpp @@ -93,7 +93,7 @@ class MockStorage : public virtual StateStorage { public: MockStorage(std::shared_ptr prev) - : storage::StateStorageInterface(prev), StateStorage(prev) + : storage::StateStorageInterface(prev), StateStorage(prev, false) {} bcos::Error::Ptr setRows(std::string_view tableName, RANGES::any_view(); - auto memoryStorage = std::make_shared(nullptr); + auto memoryStorage = std::make_shared(nullptr, false); memoryStorage->setEnableTraverse(true); auto storage = std::make_shared(memoryStorage); storage->setEnableTraverse(true); @@ -146,9 +146,9 @@ class LedgerFixture : public TestPromptFixture inline void initErrorStorage() { - auto memoryStorage = std::make_shared(nullptr); + auto memoryStorage = std::make_shared(nullptr, false); memoryStorage->setEnableTraverse(true); - auto storage = std::make_shared(memoryStorage); + auto storage = std::make_shared(memoryStorage, false); storage->setEnableTraverse(true); m_storage = storage; BOOST_TEST(m_storage != nullptr); @@ -1383,7 +1383,7 @@ BOOST_AUTO_TEST_CASE(genesisBlockWithAllocs) { task::syncWait([this]() -> task::Task { auto hashImpl = std::make_shared(); - auto memoryStorage = std::make_shared(nullptr); + auto memoryStorage = std::make_shared(nullptr, false); auto storage = std::make_shared(memoryStorage); auto ledger = std::make_shared(m_blockFactory, storage, 1); @@ -1441,7 +1441,7 @@ BOOST_AUTO_TEST_CASE(replaceBinary) { task::syncWait([this]() -> task::Task { auto hashImpl = std::make_shared(); - auto memoryStorage = std::make_shared(nullptr); + auto memoryStorage = std::make_shared(nullptr, false); auto storage = std::make_shared(memoryStorage); auto ledger = std::make_shared(m_blockFactory, storage, 1); diff --git a/bcos-pbft/test/unittests/pbft/PBFTFixture.h b/bcos-pbft/test/unittests/pbft/PBFTFixture.h index 05000b4ccc..56e3dcdbb2 100644 --- a/bcos-pbft/test/unittests/pbft/PBFTFixture.h +++ b/bcos-pbft/test/unittests/pbft/PBFTFixture.h @@ -32,12 +32,8 @@ #include #include #include -#include -// #include -// #include -// #include -// #include #include +#include #include #include #include @@ -253,7 +249,8 @@ class PBFTFixture m_frontService = std::make_shared(_keyPair->publicKey()); // create KVStorageHelper - m_storage = std::make_shared(std::make_shared(nullptr)); + m_storage = + std::make_shared(std::make_shared(nullptr, false)); // create fakeLedger if (_ledger == nullptr) @@ -262,7 +259,8 @@ class PBFTFixture m_ledger->setSystemConfig(SYSTEM_KEY_TX_COUNT_LIMIT, std::to_string(_txCountLimit)); m_ledger->setSystemConfig(SYSTEM_KEY_CONSENSUS_LEADER_PERIOD, std::to_string(1)); m_ledger->setSystemConfig(SYSTEM_KEY_AUTH_CHECK_STATUS, std::to_string(0)); - m_ledger->setSystemConfig(SYSTEM_KEY_COMPATIBILITY_VERSION, protocol::DEFAULT_VERSION_STR); + m_ledger->setSystemConfig( + SYSTEM_KEY_COMPATIBILITY_VERSION, protocol::DEFAULT_VERSION_STR); // m_ledger->ledgerConfig()->setConsensusTimeout(_consensusTimeout * 20); m_ledger->ledgerConfig()->setBlockTxCountLimit(_txCountLimit); } @@ -398,7 +396,8 @@ inline std::map createFakers(CryptoSuite::Ptr _cryp fakedLedger->setSystemConfig(SYSTEM_KEY_TX_COUNT_LIMIT, std::to_string(_txCountLimit)); fakedLedger->setSystemConfig(SYSTEM_KEY_CONSENSUS_LEADER_PERIOD, std::to_string(1)); fakedLedger->setSystemConfig(SYSTEM_KEY_AUTH_CHECK_STATUS, std::to_string(0)); - fakedLedger->setSystemConfig(SYSTEM_KEY_COMPATIBILITY_VERSION, protocol::DEFAULT_VERSION_STR); + fakedLedger->setSystemConfig( + SYSTEM_KEY_COMPATIBILITY_VERSION, protocol::DEFAULT_VERSION_STR); // fakedLedger->ledgerConfig()->setConsensusTimeout(_consensusTimeout * 1000); fakedLedger->ledgerConfig()->setBlockTxCountLimit(_txCountLimit); auto peerFaker = createPBFTFixture(_cryptoSuite, fakedLedger, _txCountLimit); diff --git a/bcos-rpbft/test/unittests/config/RPBFTConfigTest.cpp b/bcos-rpbft/test/unittests/config/RPBFTConfigTest.cpp index 73d9fa43d8..3c83ec7967 100644 --- a/bcos-rpbft/test/unittests/config/RPBFTConfigTest.cpp +++ b/bcos-rpbft/test/unittests/config/RPBFTConfigTest.cpp @@ -52,7 +52,8 @@ class RPBFTConfigFixture : public TestPromptFixture m_frontService = std::make_shared(m_nodeId); // create KVStorageHelper - m_storage = std::make_shared(std::make_shared(nullptr)); + m_storage = + std::make_shared(std::make_shared(nullptr, false)); m_ledger = std::make_shared(m_blockFactory, 20, 10, 10); m_ledger->setSystemConfig(SYSTEM_KEY_TX_COUNT_LIMIT, std::to_string(1000)); diff --git a/bcos-scheduler/src/BlockExecutive.cpp b/bcos-scheduler/src/BlockExecutive.cpp index 160f3a8d57..d0a64c68a9 100644 --- a/bcos-scheduler/src/BlockExecutive.cpp +++ b/bcos-scheduler/src/BlockExecutive.cpp @@ -2,18 +2,22 @@ #include "Common.h" #include "DmcExecutor.h" #include "SchedulerImpl.h" -#include "bcos-crypto/bcos-crypto/ChecksumAddress.h" #include "bcos-framework/executor/ExecutionMessage.h" #include "bcos-framework/executor/ParallelTransactionExecutorInterface.h" #include "bcos-framework/executor/PrecompiledTypeDef.h" +#include "bcos-framework/ledger/Features.h" #include "bcos-framework/protocol/Transaction.h" #include "bcos-table/src/StateStorage.h" #include "bcos-tars-protocol/protocol/BlockImpl.h" +#include "bcos-task/Wait.h" #include #include #include #include #include +#include +#include +#include #include #include #include @@ -23,9 +27,6 @@ #include #include #include -#include -#include -#include #include #ifdef USE_TCMALLOC @@ -438,48 +439,52 @@ void BlockExecutive::asyncExecute( void BlockExecutive::asyncCommit(std::function callback) { - auto stateStorage = std::make_shared(m_scheduler->m_storage); - - m_currentTimePoint = std::chrono::system_clock::now(); - SCHEDULER_LOG(DEBUG) << BLOCK_NUMBER(number()) << LOG_DESC("BlockExecutive commit block"); - - m_scheduler->m_ledger->asyncPrewriteBlock( - stateStorage, m_blockTxs, m_block, - [this, stateStorage, callback = std::move(callback)]( - std::string primiaryKey, Error::Ptr&& error) mutable { - if (error) - { - SCHEDULER_LOG(ERROR) << "Prewrite block error!" << error->errorMessage(); - - if (error->errorCode() == bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR) + task::wait([this](decltype(callback) callback) -> task::Task { + ledger::Features features; + co_await features.readFromStorage(*m_scheduler->m_storage, m_blockHeader->number()); + auto stateStorage = std::make_shared(m_scheduler->m_storage, + features.get(ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); + + m_currentTimePoint = std::chrono::system_clock::now(); + SCHEDULER_LOG(DEBUG) << BLOCK_NUMBER(number()) << LOG_DESC("BlockExecutive commit block"); + + m_scheduler->m_ledger->asyncPrewriteBlock( + stateStorage, m_blockTxs, m_block, + [this, stateStorage, callback = std::move(callback)]( + std::string primiaryKey, Error::Ptr&& error) mutable { + if (error) { - triggerSwitch(); - } + SCHEDULER_LOG(ERROR) << "Prewrite block error!" << error->errorMessage(); - callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(SchedulerError::PrewriteBlockError, - "Prewrite block failed: " + error->errorMessage(), *error)); + if (error->errorCode() == bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR) + { + triggerSwitch(); + } - return; - } + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(SchedulerError::PrewriteBlockError, + "Prewrite block failed: " + error->errorMessage(), *error)); - auto status = std::make_shared(); - // self + ledger(txs receipts) + executors = 1 + 1 + executors - status->total = 2 + getExecutorSize(); - status->checkAndCommit = [this, callback](const CommitStatus& status) { - if (!m_isRunning) - { - callback(BCOS_ERROR_UNIQUE_PTR( - SchedulerError::Stopped, "BlockExecutive is stopped")); return; } - if (status.failed > 0) - { - std::string errorMessage = "Prepare with errors, begin rollback, status: " + - boost::lexical_cast(status.failed); - SCHEDULER_LOG(WARNING) << BLOCK_NUMBER(number()) << errorMessage; - batchBlockRollback( - status.startTS, [this, callback, errorMessage](Error::UniquePtr&& error) { + auto status = std::make_shared(); + // self + ledger(txs receipts) + executors = 1 + 1 + executors + status->total = 2 + getExecutorSize(); + status->checkAndCommit = [this, callback](const CommitStatus& status) { + if (!m_isRunning) + { + callback(BCOS_ERROR_UNIQUE_PTR( + SchedulerError::Stopped, "BlockExecutive is stopped")); + return; + } + + if (status.failed > 0) + { + std::string errorMessage = "Prepare with errors, begin rollback, status: " + + boost::lexical_cast(status.failed); + SCHEDULER_LOG(WARNING) << BLOCK_NUMBER(number()) << errorMessage; + batchBlockRollback(status.startTS, [this, callback, errorMessage]( + Error::UniquePtr&& error) { if (error) { SCHEDULER_LOG(ERROR) @@ -498,138 +503,139 @@ void BlockExecutive::asyncCommit(std::function callback) } }); - return; - } - - SCHEDULER_LOG(DEBUG) << BLOCK_NUMBER(number()) << "batchCommitBlock begin"; - batchBlockCommit(status.startTS, [this, callback](Error::UniquePtr&& error) { - if (error) - { - SCHEDULER_LOG(WARNING) - << BLOCK_NUMBER(number()) << "Commit block to storage failed!" - << error->errorMessage(); + return; + } - if (error->errorCode() == - bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR) + SCHEDULER_LOG(DEBUG) << BLOCK_NUMBER(number()) << "batchCommitBlock begin"; + batchBlockCommit(status.startTS, [this, callback](Error::UniquePtr&& error) { + if (error) { - triggerSwitch(); - } + SCHEDULER_LOG(WARNING) + << BLOCK_NUMBER(number()) << "Commit block to storage failed!" + << error->errorMessage(); - // FATAL ERROR, NEED MANUAL FIX! - callback(std::move(error)); + if (error->errorCode() == + bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR) + { + triggerSwitch(); + } - return; - } + // FATAL ERROR, NEED MANUAL FIX! + callback(std::move(error)); - m_commitElapsed = std::chrono::duration_cast( - std::chrono::system_clock::now() - m_currentTimePoint); - SCHEDULER_LOG(DEBUG) - << BLOCK_NUMBER(number()) << "CommitBlock: " - << "success, execute elapsed: " << m_executeElapsed.count() - << "ms hash elapsed: " << m_hashElapsed.count() - << "ms commit elapsed: " << m_commitElapsed.count() << "ms"; + return; + } - callback(nullptr); - }); - }; - - bcos::protocol::TwoPCParams params; - params.number = number(); - params.primaryKey = std::move(primiaryKey); - m_scheduler->m_storage->asyncPrepare(params, *stateStorage, - [status, this, callback]( - Error::Ptr&& error, uint64_t startTimeStamp, const std::string& primaryKey) { - if (error) - { + m_commitElapsed = std::chrono::duration_cast( + std::chrono::system_clock::now() - m_currentTimePoint); + SCHEDULER_LOG(DEBUG) + << BLOCK_NUMBER(number()) << "CommitBlock: " + << "success, execute elapsed: " << m_executeElapsed.count() + << "ms hash elapsed: " << m_hashElapsed.count() + << "ms commit elapsed: " << m_commitElapsed.count() << "ms"; + + callback(nullptr); + }); + }; + + bcos::protocol::TwoPCParams params; + params.number = number(); + params.primaryKey = std::move(primiaryKey); + m_scheduler->m_storage->asyncPrepare(params, *stateStorage, + [status, this, callback](Error::Ptr&& error, uint64_t startTimeStamp, + const std::string& primaryKey) { + if (error) + { + { + WriteGuard lock(status->x_lock); + ++status->failed; + } + SCHEDULER_LOG(WARNING) << BLOCK_NUMBER(number()) + << "scheduler asyncPrepare storage error: " + << error->errorMessage(); + callback(BCOS_ERROR_UNIQUE_PTR(error->errorCode(), + "asyncPrepare block error: " + error->errorMessage())); + return; + } + else { WriteGuard lock(status->x_lock); - ++status->failed; + ++status->success; } - SCHEDULER_LOG(WARNING) - << BLOCK_NUMBER(number()) - << "scheduler asyncPrepare storage error: " << error->errorMessage(); - callback(BCOS_ERROR_UNIQUE_PTR(error->errorCode(), - "asyncPrepare block error: " + error->errorMessage())); - return; - } - else - { - WriteGuard lock(status->x_lock); - ++status->success; - } - SCHEDULER_LOG(DEBUG) - << BLOCK_NUMBER(number()) - << "primary prepare finished, call executor prepare" - << LOG_KV("startTimeStamp", startTimeStamp) - << LOG_KV("executors", getExecutorSize()) - << LOG_KV("success", status->success) << LOG_KV("failed", status->failed); - bcos::protocol::TwoPCParams executorParams; - executorParams.number = number(); - executorParams.primaryKey = primaryKey; - executorParams.timestamp = startTimeStamp; - status->startTS = startTimeStamp; - for (const auto& executorIt : *(m_scheduler->m_executorManager)) - { - executorIt->prepare(executorParams, [this, status](Error::Ptr&& error) { - { - WriteGuard lock(status->x_lock); - if (error) + SCHEDULER_LOG(DEBUG) << BLOCK_NUMBER(number()) + << "primary prepare finished, call executor prepare" + << LOG_KV("startTimeStamp", startTimeStamp) + << LOG_KV("executors", getExecutorSize()) + << LOG_KV("success", status->success) + << LOG_KV("failed", status->failed); + bcos::protocol::TwoPCParams executorParams; + executorParams.number = number(); + executorParams.primaryKey = primaryKey; + executorParams.timestamp = startTimeStamp; + status->startTS = startTimeStamp; + for (const auto& executorIt : *(m_scheduler->m_executorManager)) + { + executorIt->prepare(executorParams, [this, status](Error::Ptr&& error) { { - ++status->failed; - SCHEDULER_LOG(ERROR) - << BLOCK_NUMBER(number()) - << "asyncPrepare executor failed: " - << LOG_KV("code", error->errorCode()) << error->what(); - - if (error->errorCode() == - bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR) + WriteGuard lock(status->x_lock); + if (error) { - triggerSwitch(); + ++status->failed; + SCHEDULER_LOG(ERROR) + << BLOCK_NUMBER(number()) + << "asyncPrepare executor failed: " + << LOG_KV("code", error->errorCode()) << error->what(); + + if (error->errorCode() == + bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR) + { + triggerSwitch(); + } + } + else + { + ++status->success; + SCHEDULER_LOG(DEBUG) + << BLOCK_NUMBER(number()) + << "asyncPrepare executor success, success: " + << status->success; + } + if (status->success + status->failed < status->total) + { + return; } } - else - { - ++status->success; - SCHEDULER_LOG(DEBUG) - << BLOCK_NUMBER(number()) - << "asyncPrepare executor success, success: " - << status->success; - } - if (status->success + status->failed < status->total) - { - return; - } - } - status->checkAndCommit(*status); - }); - } - }); - // write transactions and receipts in another DB txn - auto err = m_scheduler->m_ledger->storeTransactionsAndReceipts( - m_blockTxs, std::const_pointer_cast(m_block)); - { - WriteGuard lock(status->x_lock); - if (err) - { - ++status->failed; - SCHEDULER_LOG(ERROR) - << BLOCK_NUMBER(number()) << "write txs and receipts failed: " - << LOG_KV("message", err->errorMessage()); - } - else - { - ++status->success; - } - if (status->success + status->failed < status->total) + status->checkAndCommit(*status); + }); + } + }); + // write transactions and receipts in another DB txn + auto err = m_scheduler->m_ledger->storeTransactionsAndReceipts( + m_blockTxs, std::const_pointer_cast(m_block)); { - return; + WriteGuard lock(status->x_lock); + if (err) + { + ++status->failed; + SCHEDULER_LOG(ERROR) + << BLOCK_NUMBER(number()) << "write txs and receipts failed: " + << LOG_KV("message", err->errorMessage()); + } + else + { + ++status->success; + } + if (status->success + status->failed < status->total) + { + return; + } } - } - status->checkAndCommit(*status); - }, - false); + status->checkAndCommit(*status); + }, + false); + }(std::move(callback))); } void BlockExecutive::asyncNotify( @@ -1004,14 +1010,13 @@ void BlockExecutive::onDmcExecuteFinish( auto dmcChecksum = m_dmcRecorder->dumpAndClearChecksum(); if (m_staticCall) { - DMC_LOG(TRACE) << LOG_BADGE("Stat") << "DMCExecute.6:" - << "\t " << LOG_BADGE("DMCRecorder") << " DMCExecute for call finished " - << LOG_KV("blockNumber", number()) << LOG_KV("checksum", dmcChecksum); + DMC_LOG(TRACE) << LOG_BADGE("Stat") << "DMCExecute.6:" << "\t " << LOG_BADGE("DMCRecorder") + << " DMCExecute for call finished " << LOG_KV("blockNumber", number()) + << LOG_KV("checksum", dmcChecksum); } else { - DMC_LOG(INFO) << LOG_BADGE("Stat") << "DMCExecute.6:" - << "\t " << LOG_BADGE("DMCRecorder") + DMC_LOG(INFO) << LOG_BADGE("Stat") << "DMCExecute.6:" << "\t " << LOG_BADGE("DMCRecorder") << " DMCExecute for transaction finished " << LOG_KV("blockNumber", number()) << LOG_KV("checksum", dmcChecksum); diff --git a/bcos-scheduler/src/ShardingBlockExecutive.cpp b/bcos-scheduler/src/ShardingBlockExecutive.cpp index 85c1770cf6..6f0caf1965 100644 --- a/bcos-scheduler/src/ShardingBlockExecutive.cpp +++ b/bcos-scheduler/src/ShardingBlockExecutive.cpp @@ -287,7 +287,7 @@ std::string ShardingBlockExecutive::getContractShard(const std::string& contract } else { - stateStorage = std::make_shared(getStorage()); + stateStorage = std::make_shared(getStorage(), false); } diff --git a/bcos-storage/test/unittest/StorageTest.cpp b/bcos-storage/test/unittest/StorageTest.cpp index f691562703..00f1c93937 100644 --- a/bcos-storage/test/unittest/StorageTest.cpp +++ b/bcos-storage/test/unittest/StorageTest.cpp @@ -6,7 +6,7 @@ using namespace bcos::storage; struct StorageSyncWrapperFixture { - StorageSyncWrapperFixture() : storage(std::make_shared(nullptr)) {} + StorageSyncWrapperFixture() : storage(std::make_shared(nullptr), false) {} StorageImpl storage; }; diff --git a/bcos-storage/test/unittest/TestRocksDBStorage.cpp b/bcos-storage/test/unittest/TestRocksDBStorage.cpp index 2b9e9b1545..45b0a9ceb4 100644 --- a/bcos-storage/test/unittest/TestRocksDBStorage.cpp +++ b/bcos-storage/test/unittest/TestRocksDBStorage.cpp @@ -1,7 +1,6 @@ #include "bcos-crypto/hasher/OpenSSLHasher.h" #include "bcos-framework/storage/StorageInterface.h" #include "bcos-table/src/StateStorage.h" -#include "boost/filesystem.hpp" #include #include #include @@ -126,7 +125,7 @@ struct TestRocksDBStorageFixture auto storage = rocksDBStorage; size_t tableEntries = count; auto hashImpl = std::make_shared(); - auto stateStorage = std::make_shared(storage); + auto stateStorage = std::make_shared(storage, false); auto testTable = stateStorage->openTable(testTableName); BOOST_CHECK_EQUAL(testTable.has_value(), true); for (size_t i = 0; i < tableEntries; ++i) @@ -408,7 +407,7 @@ BOOST_AUTO_TEST_CASE(asyncPrepare) prepareTestTableData(); auto hashImpl = std::make_shared(); - auto storage = std::make_shared(rocksDBStorage); + auto storage = std::make_shared(rocksDBStorage, false); BOOST_CHECK(storage->createTable("table1", "value1,value2,value3")); BOOST_CHECK(storage->createTable("table2", "value1,value2,value3,value4,value5")); @@ -595,7 +594,7 @@ BOOST_AUTO_TEST_CASE(writeReadDelete_1Table) BOOST_AUTO_TEST_CASE(commitAndCheck) { - auto initState = std::make_shared(rocksDBStorage); + auto initState = std::make_shared(rocksDBStorage, false); initState->asyncCreateTable( "test_table1", "value", [](Error::UniquePtr error, std::optional table) { @@ -637,7 +636,7 @@ BOOST_AUTO_TEST_CASE(commitAndCheck) for (size_t i = 100; i < 1000; i += 100) { - auto state = std::make_shared(rocksDBStorage); + auto state = std::make_shared(rocksDBStorage, false); STORAGE_LOG(INFO) << "Expected: " << i; for (size_t keyIndex = 0; keyIndex < 100; ++keyIndex) diff --git a/bcos-storage/test/unittest/TestTiKVStorage.cpp b/bcos-storage/test/unittest/TestTiKVStorage.cpp index 0eb36e7b81..815f316551 100644 --- a/bcos-storage/test/unittest/TestTiKVStorage.cpp +++ b/bcos-storage/test/unittest/TestTiKVStorage.cpp @@ -1,13 +1,13 @@ #include "bcos-framework/storage/StorageInterface.h" #include "bcos-storage/TiKVStorage.h" #include "bcos-table/src/StateStorage.h" -#include "boost/filesystem.hpp" #include #include #include #include #include #include +#include #include #include #include @@ -100,7 +100,7 @@ struct TestTiKVStorageFixture { size_t tableEntries = count; auto hashImpl = std::make_shared(); - auto stateStorage = std::make_shared(storage); + auto stateStorage = std::make_shared(storage, false); auto testTable = stateStorage->openTable(testTableName); BOOST_CHECK_EQUAL(testTable.has_value(), true); for (size_t i = 0; i < tableEntries; ++i) @@ -201,13 +201,11 @@ BOOST_AUTO_TEST_CASE(asyncGetRow) { prepareTestTableData(); -#pragma omp parallel for for (size_t i = 0; i < 1050; ++i) { std::string key = "key" + boost::lexical_cast(i); storage->asyncGetRow( testTableName, key, [&](Error::UniquePtr error, std::optional entry) { -#pragma omp critical BOOST_CHECK_EQUAL(error.get(), nullptr); if (i < total) { @@ -384,7 +382,7 @@ BOOST_AUTO_TEST_CASE(asyncPrepare) prepareTestTableData(); auto hashImpl = std::make_shared(); - auto stateStorage = std::make_shared(storage); + auto stateStorage = std::make_shared(storage, false); auto table1Name = "table1"; auto table2Name = "table2"; BOOST_CHECK_EQUAL( @@ -488,7 +486,7 @@ BOOST_AUTO_TEST_CASE(asyncPrepareTimeout) prepareTestTableData(); auto hashImpl = std::make_shared(); - auto stateStorage = std::make_shared(storage); + auto stateStorage = std::make_shared(storage, false); auto table1Name = "table1"; auto table2Name = "table2"; BOOST_CHECK_EQUAL( @@ -544,7 +542,7 @@ BOOST_AUTO_TEST_CASE(multiStorageCommit) auto storage2 = std::make_shared(m_cluster); auto storage3 = std::make_shared(m_cluster); auto hashImpl = std::make_shared(); - auto stateStorage = std::make_shared(storage); + auto stateStorage = std::make_shared(storage, false); auto testTable = stateStorage->openTable(testTableName); BOOST_CHECK_EQUAL(testTable.has_value(), true); for (size_t i = 0; i < total; ++i) @@ -554,8 +552,8 @@ BOOST_AUTO_TEST_CASE(multiStorageCommit) entry.importFields({"value_" + boost::lexical_cast(i)}); testTable->setRow(key, std::move(entry)); } - auto stateStorage2 = std::make_shared(storage2); - auto stateStorage3 = std::make_shared(storage3); + auto stateStorage2 = std::make_shared(storage2, false); + auto stateStorage3 = std::make_shared(storage3, false); auto table1Name = "table1"; auto table2Name = "table2"; BOOST_CHECK_EQUAL( @@ -589,7 +587,7 @@ BOOST_AUTO_TEST_CASE(multiStorageCommit) auto params1 = bcos::protocol::TwoPCParams(); params1.number = 100; params1.primaryKey = testTableName + ":key0"; - auto stateStorage0 = std::make_shared(storage); + auto stateStorage0 = std::make_shared(storage, false); // check empty storage error storage->asyncPrepare( params1, *stateStorage0, [&](Error::Ptr error, uint64_t ts, const std::string&) { @@ -714,7 +712,7 @@ BOOST_AUTO_TEST_CASE(singleStorageRollback) BOOST_CHECK_EQUAL(error.get(), nullptr); BOOST_CHECK_EQUAL(keys.size(), 0); }); - auto stateStorage = std::make_shared(storage); + auto stateStorage = std::make_shared(storage, false); BOOST_CHECK_EQUAL( stateStorage->createTable(table1Name, "value1,value2,value3").has_value(), true); auto table1 = stateStorage->openTable(table1Name); @@ -751,7 +749,7 @@ BOOST_AUTO_TEST_CASE(multiStorageRollback) auto storage2 = std::make_shared(m_cluster); auto storage3 = std::make_shared(m_cluster); auto hashImpl = std::make_shared(); - auto stateStorage = std::make_shared(storage); + auto stateStorage = std::make_shared(storage, false); auto testTable = stateStorage->openTable(testTableName); BOOST_CHECK_EQUAL(testTable.has_value(), true); for (size_t i = 0; i < total; ++i) @@ -761,8 +759,8 @@ BOOST_AUTO_TEST_CASE(multiStorageRollback) entry.importFields({"value_" + boost::lexical_cast(i)}); testTable->setRow(key, std::move(entry)); } - auto stateStorage2 = std::make_shared(storage2); - auto stateStorage3 = std::make_shared(storage3); + auto stateStorage2 = std::make_shared(storage2, false); + auto stateStorage3 = std::make_shared(storage3, false); auto table1Name = "table1"; auto table2Name = "table2"; BOOST_CHECK_EQUAL( @@ -796,7 +794,7 @@ BOOST_AUTO_TEST_CASE(multiStorageRollback) auto params1 = bcos::protocol::TwoPCParams(); params1.number = 100; params1.primaryKey = testTableName + ":key0"; - auto stateStorage0 = std::make_shared(storage); + auto stateStorage0 = std::make_shared(storage, false); // check empty storage error storage->asyncPrepare( params1, *stateStorage0, [&](Error::Ptr error, uint64_t ts, const std::string&) { @@ -845,7 +843,7 @@ BOOST_AUTO_TEST_CASE(secondaryRollbackAndPrimaryCommit) auto storage2 = std::make_shared(m_cluster); auto storage3 = std::make_shared(m_cluster); auto hashImpl = std::make_shared(); - auto stateStorage = std::make_shared(storage); + auto stateStorage = std::make_shared(storage, false); auto testTable = stateStorage->openTable(testTableName); BOOST_CHECK_EQUAL(testTable.has_value(), true); for (size_t i = 0; i < total; ++i) @@ -855,7 +853,7 @@ BOOST_AUTO_TEST_CASE(secondaryRollbackAndPrimaryCommit) entry.importFields({"value_" + boost::lexical_cast(i)}); testTable->setRow(key, std::move(entry)); } - auto stateStorage1 = std::make_shared(storage2); + auto stateStorage1 = std::make_shared(storage2, false); auto table1Name = "table1"; BOOST_CHECK_EQUAL( stateStorage1->createTable(table1Name, "value1,value2,value3").has_value(), true); @@ -876,7 +874,7 @@ BOOST_AUTO_TEST_CASE(secondaryRollbackAndPrimaryCommit) auto params1 = bcos::protocol::TwoPCParams(); params1.number = 100; params1.primaryKey = testTableName + ":key0"; - auto stateStorage0 = std::make_shared(storage); + auto stateStorage0 = std::make_shared(storage, false); // prewrite storage->asyncPrepare( @@ -917,7 +915,7 @@ BOOST_AUTO_TEST_CASE(multiStorageScondaryCrash) auto storage2 = std::make_shared(m_cluster); auto storage3 = std::make_shared(m_cluster); auto hashImpl = std::make_shared(); - auto stateStorage = std::make_shared(storage); + auto stateStorage = std::make_shared(storage, false); auto testTable = stateStorage->openTable(testTableName); BOOST_CHECK_EQUAL(testTable.has_value(), true); for (size_t i = 0; i < total; ++i) @@ -927,8 +925,8 @@ BOOST_AUTO_TEST_CASE(multiStorageScondaryCrash) entry.importFields({"value_" + boost::lexical_cast(i)}); testTable->setRow(key, std::move(entry)); } - auto stateStorage2 = std::make_shared(storage2); - auto stateStorage3 = std::make_shared(storage3); + auto stateStorage2 = std::make_shared(storage2, false); + auto stateStorage3 = std::make_shared(storage3, false); auto table1Name = "table1"; auto table2Name = "table2"; BOOST_CHECK_EQUAL( @@ -962,7 +960,7 @@ BOOST_AUTO_TEST_CASE(multiStorageScondaryCrash) auto params1 = bcos::protocol::TwoPCParams(); params1.number = 100; params1.primaryKey = testTableName + ":key0"; - auto stateStorage0 = std::make_shared(storage); + auto stateStorage0 = std::make_shared(storage, false); // check empty storage error storage->asyncPrepare( params1, *stateStorage0, [&](Error::Ptr error, uint64_t ts, const std::string&) { @@ -1142,7 +1140,7 @@ BOOST_AUTO_TEST_CASE(multiStoragePrimaryCrash) auto storage2 = std::make_shared(m_cluster); auto storage3 = std::make_shared(m_cluster); auto hashImpl = std::make_shared(); - auto stateStorage = std::make_shared(storage); + auto stateStorage = std::make_shared(storage, false); auto testTable = stateStorage->openTable(testTableName); BOOST_CHECK_EQUAL(testTable.has_value(), true); for (size_t i = 0; i < total; ++i) @@ -1152,8 +1150,8 @@ BOOST_AUTO_TEST_CASE(multiStoragePrimaryCrash) entry.importFields({"value_" + boost::lexical_cast(i)}); testTable->setRow(key, std::move(entry)); } - auto stateStorage2 = std::make_shared(storage2); - auto stateStorage3 = std::make_shared(storage3); + auto stateStorage2 = std::make_shared(storage2, false); + auto stateStorage3 = std::make_shared(storage3, false); auto table1Name = "table1"; auto table2Name = "table2"; BOOST_CHECK_EQUAL( @@ -1187,7 +1185,7 @@ BOOST_AUTO_TEST_CASE(multiStoragePrimaryCrash) auto params1 = bcos::protocol::TwoPCParams(); params1.number = 100; params1.primaryKey = testTableName + ":key0"; - auto stateStorage0 = std::make_shared(storage); + auto stateStorage0 = std::make_shared(storage, false); // check empty storage error storage->asyncPrepare( params1, *stateStorage0, [&](Error::Ptr error, uint64_t ts, const std::string&) { @@ -1213,7 +1211,7 @@ BOOST_AUTO_TEST_CASE(multiStoragePrimaryCrash) // just recommit prewrite storage = std::make_shared(m_cluster); auto storage4 = std::make_shared(m_cluster); - auto stateStorage4 = std::make_shared(storage3); + auto stateStorage4 = std::make_shared(storage3, false); params1.timestamp = 0; storage->asyncPrepare( params1, *stateStorage, [&](Error::Ptr error, uint64_t ts, const std::string&) { diff --git a/bcos-table/src/CacheStorageFactory.cpp b/bcos-table/src/CacheStorageFactory.cpp index 415bb20bd0..efbe7ba504 100644 --- a/bcos-table/src/CacheStorageFactory.cpp +++ b/bcos-table/src/CacheStorageFactory.cpp @@ -5,7 +5,7 @@ using namespace bcos::storage; MergeableStorageInterface::Ptr CacheStorageFactory::build() { - auto cache = std::make_shared(m_backendStorage); + auto cache = std::make_shared(m_backendStorage, false); cache->setMaxCapacity(m_cacheSize); BCOS_LOG(INFO) << "Build CacheStorage: enableLRUCacheStorage, size: " << m_cacheSize; diff --git a/bcos-table/src/StateStorage.h b/bcos-table/src/StateStorage.h index 6266554fdd..8767f18f45 100644 --- a/bcos-table/src/StateStorage.h +++ b/bcos-table/src/StateStorage.h @@ -25,7 +25,6 @@ #pragma once #include "StateStorageInterface.h" -#include "bcos-framework/storage/Table.h" #include #include #include @@ -49,8 +48,10 @@ class BaseStorage : public virtual storage::StateStorageInterface, public: using Ptr = std::shared_ptr>; - explicit BaseStorage(std::shared_ptr prev) - : storage::StateStorageInterface(prev), m_buckets(std::thread::hardware_concurrency()) + BaseStorage(std::shared_ptr prev, bool setRowWithDirtyFlag) + : storage::StateStorageInterface(prev), + m_buckets(std::thread::hardware_concurrency()), + m_setRowWithDirtyFlag(setRowWithDirtyFlag) {} BaseStorage(const BaseStorage&) = delete; @@ -312,9 +313,11 @@ class BaseStorage : public virtual storage::StateStorageInterface, ssize_t updatedCapacity = entry.size(); std::optional entryOld; + if (m_setRowWithDirtyFlag && entry.status() == Entry::NORMAL) + { + entry.setStatus(Entry::MODIFIED); + } auto [bucket, lock] = getBucket(tableView, keyView); - boost::ignore_unused(lock); - auto it = bucket->container.find(std::make_tuple(tableView, keyView)); if (it != bucket->container.end()) { @@ -591,6 +594,7 @@ class BaseStorage : public virtual storage::StateStorageInterface, }; uint32_t m_blockVersion = 0; std::vector m_buckets; + bool m_setRowWithDirtyFlag = false; std::tuple> getBucket( const std::string_view& table, const std::string_view& key) diff --git a/bcos-table/src/StateStorageFactory.h b/bcos-table/src/StateStorageFactory.h index 949baebd90..582baf1745 100644 --- a/bcos-table/src/StateStorageFactory.h +++ b/bcos-table/src/StateStorageFactory.h @@ -29,8 +29,6 @@ #include #include #include -#include - namespace bcos::storage { @@ -66,13 +64,14 @@ class StateStorageFactory virtual storage::StateStorageInterface::Ptr createStateStorage( bcos::storage::StorageInterface::Ptr storage, uint32_t compatibilityVersion, - bool ignoreNotExist = false, + bool setRowWithDirtyFlag, bool ignoreNotExist = false, std::shared_ptr>> const& keyPageIgnoreTables = nullptr) { STORAGE_LOG(TRACE) << LOG_KV("compatibilityVersion", compatibilityVersion) << LOG_KV("protocol::BlockVersion::V3_1_VERSION", (uint32_t)protocol::BlockVersion::V3_1_VERSION) - << LOG_KV("keyPageSize", m_keyPageSize); + << LOG_KV("keyPageSize", m_keyPageSize) + << LOG_KV("setRowWithDirtyFlag", setRowWithDirtyFlag); if (m_keyPageSize > 0) { @@ -100,7 +99,7 @@ class StateStorageFactory } // Pass useHashV310 flag to hash() insted of compatibilityVersion - return std::make_shared(storage); + return std::make_shared(storage, setRowWithDirtyFlag); } private: diff --git a/bcos-table/test/unittests/libtable/Table.cpp b/bcos-table/test/unittests/libtable/Table.cpp index cc7d6084a9..2280cd5245 100644 --- a/bcos-table/test/unittests/libtable/Table.cpp +++ b/bcos-table/test/unittests/libtable/Table.cpp @@ -65,8 +65,8 @@ struct TableFixture TableFixture() { hashImpl = make_shared(); - memoryStorage = make_shared(nullptr); - tableFactory = make_shared(memoryStorage); + memoryStorage = make_shared(nullptr, false); + tableFactory = make_shared(memoryStorage, false); } ~TableFixture() {} @@ -81,7 +81,7 @@ BOOST_AUTO_TEST_CASE(constructor) { auto threadPool = ThreadPool("a", 1); auto table = std::make_shared
(nullptr, nullptr); - auto tableFactory = std::make_shared(memoryStorage); + auto tableFactory = std::make_shared(nullptr, false); } BOOST_AUTO_TEST_CASE(tableInfo) @@ -226,7 +226,7 @@ BOOST_AUTO_TEST_CASE(removeFromCache) auto hashs = tableFactory->hash(hashImpl, ledger::Features()); - auto tableFactory2 = std::make_shared(nullptr); + auto tableFactory2 = std::make_shared(nullptr, false); BOOST_CHECK(tableFactory2->createTable(tableName, valueField)); auto table2 = tableFactory2->openTable(tableName); BOOST_TEST(table2); diff --git a/bcos-table/test/unittests/libtable/TablePerf.cpp b/bcos-table/test/unittests/libtable/TablePerf.cpp index 52c363709d..46cda9a951 100644 --- a/bcos-table/test/unittests/libtable/TablePerf.cpp +++ b/bcos-table/test/unittests/libtable/TablePerf.cpp @@ -15,9 +15,9 @@ struct TablePerfFixture TablePerfFixture() { // auto hashImpl = std::make_shared(); - auto memoryStorage = std::make_shared(nullptr); + auto memoryStorage = std::make_shared(nullptr, false); BOOST_TEST(memoryStorage != nullptr); - tableFactory = std::make_shared(memoryStorage); + tableFactory = std::make_shared(nullptr, false); BOOST_TEST(tableFactory != nullptr); } diff --git a/bcos-table/test/unittests/libtable/TestKeyPageStorage.cpp b/bcos-table/test/unittests/libtable/TestKeyPageStorage.cpp index f8a67be711..847cab77ed 100644 --- a/bcos-table/test/unittests/libtable/TestKeyPageStorage.cpp +++ b/bcos-table/test/unittests/libtable/TestKeyPageStorage.cpp @@ -86,7 +86,7 @@ struct KeyPageStorageFixture { boost::log::core::get()->set_logging_enabled(false); hashImpl = make_shared(); - auto stateStorage = make_shared(nullptr); + auto stateStorage = make_shared(nullptr, false); stateStorage->setEnableTraverse(true); memoryStorage = stateStorage; BOOST_REQUIRE(memoryStorage != nullptr); @@ -621,7 +621,7 @@ BOOST_AUTO_TEST_CASE(hash) BOOST_AUTO_TEST_CASE(hash_V3_1_0) { auto hashImpl2 = make_shared(); - auto memoryStorage2 = make_shared(nullptr); + auto memoryStorage2 = make_shared(nullptr, false); auto tableFactory2 = make_shared( memoryStorage2, 10240, (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION); auto tableFactory1 = make_shared( @@ -684,7 +684,7 @@ BOOST_AUTO_TEST_CASE(hash_V3_1_0) BOOST_AUTO_TEST_CASE(hash_different_table_same_data) { auto hashImpl2 = std::make_shared(); - auto memoryStorage2 = make_shared(nullptr); + auto memoryStorage2 = make_shared(nullptr, false); auto tableFactory1 = make_shared( memoryStorage2, 10240, (uint32_t)bcos::protocol::BlockVersion::V3_0_VERSION); @@ -752,7 +752,7 @@ BOOST_AUTO_TEST_CASE(open_sysTables) BOOST_AUTO_TEST_CASE(openAndCommit) { auto hashImpl2 = make_shared(); - auto memoryStorage2 = make_shared(nullptr); + auto memoryStorage2 = make_shared(nullptr, false); auto tableFactory2 = make_shared(memoryStorage2); for (int i = 10; i < 20; ++i) @@ -783,7 +783,7 @@ BOOST_AUTO_TEST_CASE(openAndCommit) BOOST_AUTO_TEST_CASE(checkInvalidKeys) { auto hashImpl2 = make_shared(); - auto memoryStorage2 = make_shared(nullptr); + auto memoryStorage2 = make_shared(nullptr, false); auto tableFactory2 = make_shared(memoryStorage2); BOOST_REQUIRE(tableFactory2 != nullptr); @@ -832,7 +832,7 @@ BOOST_AUTO_TEST_CASE(chainLink) std::vector storages; auto valueFields = "value1"; - auto stateStorage = make_shared(nullptr); + auto stateStorage = make_shared(nullptr, false); StateStorageInterface::Ptr prev = stateStorage; for (int i = 0; i < 10; ++i) { @@ -992,7 +992,7 @@ BOOST_AUTO_TEST_CASE(getRows) std::vector storages; auto valueFields = "value1,value2,value3"; - auto stateStorage = make_shared(nullptr); + auto stateStorage = make_shared(nullptr, false); auto prev = std::make_shared(stateStorage); auto tableStorage = std::make_shared(prev); @@ -1154,7 +1154,7 @@ BOOST_AUTO_TEST_CASE(checkVersion) BOOST_AUTO_TEST_CASE(deleteAndGetRows) { KeyPageStorage::Ptr storage1 = - std::make_shared(make_shared(nullptr)); + std::make_shared(make_shared(nullptr, false), false); storage1->asyncCreateTable( "table", "value", [](Error::UniquePtr error, std::optional
table) { @@ -1291,7 +1291,7 @@ BOOST_AUTO_TEST_CASE(readPageWithInvalidKeyAndDeleteNotChangePageKey) BOOST_AUTO_TEST_CASE(deletedAndGetRow) { KeyPageStorage::Ptr storage1 = - std::make_shared(make_shared(nullptr)); + std::make_shared(make_shared(nullptr, false), false); storage1->asyncCreateTable( "table", "value", [](Error::UniquePtr error, std::optional
table) { @@ -1325,7 +1325,7 @@ BOOST_AUTO_TEST_CASE(deletedAndGetRow) BOOST_AUTO_TEST_CASE(deletedAndGetRows) { KeyPageStorage::Ptr storage1 = - std::make_shared(make_shared(nullptr)); + std::make_shared(make_shared(nullptr, false), false); storage1->asyncCreateTable( "table", "value", [](Error::UniquePtr error, std::optional
table) { @@ -1357,7 +1357,7 @@ BOOST_AUTO_TEST_CASE(deletedAndGetRows) BOOST_AUTO_TEST_CASE(rollbackAndGetRow) { KeyPageStorage::Ptr storage1 = - std::make_shared(make_shared(nullptr)); + std::make_shared(make_shared(nullptr, false), false); storage1->asyncCreateTable( "table", "value", [](Error::UniquePtr error, std::optional
table) { @@ -1398,7 +1398,7 @@ BOOST_AUTO_TEST_CASE(rollbackAndGetRow) BOOST_AUTO_TEST_CASE(rollbackAndGetRows) { KeyPageStorage::Ptr storage1 = - std::make_shared(make_shared(nullptr)); + std::make_shared(make_shared(nullptr, false), false); storage1->asyncCreateTable( "table", "value", [](Error::UniquePtr error, std::optional
table) { @@ -1473,7 +1473,7 @@ BOOST_AUTO_TEST_CASE(randomRWHash) for (size_t times = 0; times < 10; ++times) { std::vector hashes; - StateStorageInterface::Ptr prev = make_shared(nullptr); + StateStorageInterface::Ptr prev = make_shared(nullptr, false); for (size_t i = 0; i < 10; ++i) { KeyPageStorage::Ptr storage = std::make_shared(prev); @@ -1605,7 +1605,7 @@ BOOST_AUTO_TEST_CASE(pageMerge) { auto valueFields = "value1"; - auto stateStorage = make_shared(nullptr); + auto stateStorage = make_shared(nullptr, false); StateStorageInterface::Ptr prev = stateStorage; auto tableStorage = std::make_shared(prev, 1024); @@ -1681,7 +1681,7 @@ BOOST_AUTO_TEST_CASE(pageMergeRandom) { auto valueFields = "value1"; - auto stateStorage = make_shared(nullptr); + auto stateStorage = make_shared(nullptr, false); StateStorageInterface::Ptr prev = stateStorage; auto tableStorage = std::make_shared(prev, 1024); @@ -1795,7 +1795,7 @@ BOOST_AUTO_TEST_CASE(pageMergeParallelRandom) { auto valueFields = "value1"; - auto stateStorage = make_shared(nullptr); + auto stateStorage = make_shared(nullptr, false); StateStorageInterface::Ptr prev = stateStorage; auto tableStorage = std::make_shared(prev, 1024); @@ -1880,7 +1880,7 @@ BOOST_AUTO_TEST_CASE(parallelMix) { auto valueFields = "value1"; - auto stateStorage = make_shared(nullptr); + auto stateStorage = make_shared(nullptr, false); StateStorageInterface::Ptr prev = stateStorage; auto tableStorage = std::make_shared(prev, 1024); @@ -1977,7 +1977,7 @@ BOOST_AUTO_TEST_CASE(pageSplit) { auto valueFields = "value1"; - auto stateStorage = make_shared(nullptr); + auto stateStorage = make_shared(nullptr, false); StateStorageInterface::Ptr prev = stateStorage; auto tableStorage = std::make_shared(prev, 1024); @@ -2027,7 +2027,7 @@ BOOST_AUTO_TEST_CASE(pageSplitRandom) { auto valueFields = "value1"; - auto stateStorage = make_shared(nullptr); + auto stateStorage = make_shared(nullptr, false); StateStorageInterface::Ptr prev = stateStorage; auto tableStorage = std::make_shared(prev, 256); @@ -2083,7 +2083,7 @@ BOOST_AUTO_TEST_CASE(pageSplitParallelRandom) { auto valueFields = "value1"; - auto stateStorage = make_shared(nullptr); + auto stateStorage = make_shared(nullptr, false); StateStorageInterface::Ptr prev = stateStorage; auto tableStorage = std::make_shared(prev, 256); @@ -2139,7 +2139,7 @@ BOOST_AUTO_TEST_CASE(asyncGetPrimaryKeys) { // TODO: add ut for asyncGetPrimaryKeys and condition auto valueFields = "value1"; - auto stateStorage = make_shared(nullptr); + auto stateStorage = make_shared(nullptr, false); StateStorageInterface::Ptr prev = stateStorage; auto tableStorage = std::make_shared(prev, 256); @@ -2246,14 +2246,14 @@ BOOST_AUTO_TEST_CASE(BigTableAdd) auto valueFields = "value1"; auto cacheSize = 256 * 1024 * 1024; auto pageSize = 512; - auto stateStorage0 = make_shared(nullptr); + auto stateStorage0 = make_shared(nullptr, false); stateStorage0->setMaxCapacity(cacheSize); StateStorageInterface::Ptr prev0 = stateStorage0; auto tableName = "table_0"; BOOST_REQUIRE(prev0->createTable(tableName, valueFields)); - auto stateStorage1 = make_shared(nullptr); + auto stateStorage1 = make_shared(nullptr, false); stateStorage1->setMaxCapacity(cacheSize); StateStorageInterface::Ptr prev1 = stateStorage1; @@ -2323,14 +2323,14 @@ BOOST_AUTO_TEST_CASE(BigTableAddSerialize) auto valueFields = "value1"; auto cacheSize = 256 * 1024 * 1024; auto pageSize = 512; - auto stateStorage0 = make_shared(nullptr); + auto stateStorage0 = make_shared(nullptr, false); stateStorage0->setMaxCapacity(cacheSize); StateStorageInterface::Ptr prev0 = stateStorage0; auto tableName = "table_0"; BOOST_REQUIRE(prev0->createTable(tableName, valueFields)); - auto stateStorage1 = make_shared(nullptr); + auto stateStorage1 = make_shared(nullptr, false); stateStorage1->setMaxCapacity(cacheSize); StateStorageInterface::Ptr prev1 = stateStorage1; @@ -2396,19 +2396,19 @@ BOOST_AUTO_TEST_CASE(mockCommitProcess) auto valueFields = "value1"; auto cacheSize = 256 * 1024 * 1024; auto pageSize = 512; - auto stateStorage0 = make_shared(nullptr); + auto stateStorage0 = make_shared(nullptr, false); stateStorage0->setMaxCapacity(cacheSize); StateStorageInterface::Ptr prev0 = stateStorage0; auto tableName = "table_0"; BOOST_REQUIRE(prev0->createTable(tableName, valueFields)); - auto stateStorage1 = make_shared(nullptr); + auto stateStorage1 = make_shared(nullptr, false); stateStorage1->setMaxCapacity(cacheSize); StateStorageInterface::Ptr prev1 = stateStorage1; BOOST_REQUIRE(prev1->createTable(tableName, valueFields)); - auto stateStorage2 = make_shared(nullptr); + auto stateStorage2 = make_shared(nullptr, false); stateStorage2->setMaxCapacity(cacheSize); StateStorageInterface::Ptr prev2 = stateStorage2; BOOST_REQUIRE(prev2->createTable(tableName, valueFields)); @@ -2511,19 +2511,19 @@ BOOST_AUTO_TEST_CASE(mockCommitProcessParallel) auto valueFields = "value1"; auto cacheSize = 256 * 1024 * 1024; auto pageSize = 512; - auto stateStorage0 = make_shared(nullptr); + auto stateStorage0 = make_shared(nullptr, false); stateStorage0->setMaxCapacity(cacheSize); StateStorageInterface::Ptr prev0 = stateStorage0; auto tableName = "table_0"; BOOST_REQUIRE(prev0->createTable(tableName, valueFields)); - auto stateStorage1 = make_shared(nullptr); + auto stateStorage1 = make_shared(nullptr, false); stateStorage1->setMaxCapacity(cacheSize); StateStorageInterface::Ptr prev1 = stateStorage1; BOOST_REQUIRE(prev1->createTable(tableName, valueFields)); - auto stateStorage2 = make_shared(nullptr); + auto stateStorage2 = make_shared(nullptr, false); stateStorage2->setMaxCapacity(cacheSize); StateStorageInterface::Ptr prev2 = stateStorage2; BOOST_REQUIRE(prev2->createTable(tableName, valueFields)); @@ -2628,7 +2628,7 @@ BOOST_AUTO_TEST_CASE(pageMergeBig) { auto valueFields = "value1"; - auto stateStorage = make_shared(nullptr); + auto stateStorage = make_shared(nullptr, false); StateStorageInterface::Ptr prev = stateStorage; auto tableStorage = std::make_shared(prev, 1024); @@ -2776,7 +2776,7 @@ BOOST_AUTO_TEST_CASE(insertAndDelete) auto valueFields = "value1"; auto cacheSize = 256 * 1024 * 1024; auto pageSize = 512; - auto stateStorage0 = make_shared(nullptr); + auto stateStorage0 = make_shared(nullptr, false); stateStorage0->setMaxCapacity(cacheSize); StateStorageInterface::Ptr prev0 = stateStorage0; @@ -2857,7 +2857,7 @@ BOOST_AUTO_TEST_CASE(invalidPageKeyToValid) boost::log::core::get()->set_logging_enabled(true); auto valueFields = "value1"; - auto stateStorage = make_shared(nullptr); + auto stateStorage = make_shared(nullptr, false); StateStorageInterface::Ptr prev = stateStorage; auto tableStorage = std::make_shared(prev, 2048); @@ -2944,7 +2944,7 @@ BOOST_AUTO_TEST_CASE(DeleteTableToEmpty_InsertInvalidPageKey) boost::log::core::get()->set_logging_enabled(true); auto valueFields = "value1"; - auto stateStorage = make_shared(nullptr); + auto stateStorage = make_shared(nullptr, false); StateStorageInterface::Ptr prev = stateStorage; auto tableStorage = std::make_shared(prev, 2048); @@ -3056,7 +3056,7 @@ BOOST_AUTO_TEST_CASE(bugfix_keypage_system_entry_hash) auto valueFields = "value1"; auto cacheSize = 256 * 1024 * 1024; auto pageSize = 512; - auto stateStorage0 = make_shared(nullptr); + auto stateStorage0 = make_shared(nullptr, false); stateStorage0->setMaxCapacity(cacheSize); StateStorageInterface::Ptr prev0 = stateStorage0; @@ -3080,7 +3080,7 @@ BOOST_AUTO_TEST_CASE(bugfix_keypage_system_entry_hash) }; auto keypage = std::make_shared( prev0, pageSize, (uint32_t)protocol::BlockVersion::V3_6_1_VERSION); - auto state = std::make_shared(prev0); + auto state = std::make_shared(prev0, false); auto hash0 = getHashLambda(keypage, features); auto hash1 = getHashLambda(state, features); BOOST_TEST(hash0.hex() != hash1.hex()); diff --git a/bcos-table/test/unittests/libtable/TestStateStorage.cpp b/bcos-table/test/unittests/libtable/TestStateStorage.cpp index 8825d18fae..e1f8484502 100644 --- a/bcos-table/test/unittests/libtable/TestStateStorage.cpp +++ b/bcos-table/test/unittests/libtable/TestStateStorage.cpp @@ -79,9 +79,9 @@ struct TableFactoryFixture TableFactoryFixture() { hashImpl = make_shared(); - memoryStorage = make_shared(nullptr); + memoryStorage = make_shared(nullptr, false); BOOST_TEST(memoryStorage != nullptr); - tableFactory = make_shared(memoryStorage); + tableFactory = make_shared(memoryStorage, false); BOOST_TEST(tableFactory != nullptr); } @@ -111,7 +111,7 @@ BOOST_FIXTURE_TEST_SUITE(StateStorageTest, TableFactoryFixture) BOOST_AUTO_TEST_CASE(constructor) { auto threadPool = ThreadPool("a", 1); - auto tf = std::make_shared(memoryStorage); + auto tf = std::make_shared(memoryStorage, false); } BOOST_AUTO_TEST_CASE(create_Table) @@ -532,8 +532,8 @@ BOOST_AUTO_TEST_CASE(open_sysTables) BOOST_AUTO_TEST_CASE(openAndCommit) { auto hashImpl2 = make_shared(); - auto memoryStorage2 = make_shared(nullptr); - auto tableFactory2 = make_shared(memoryStorage2); + auto memoryStorage2 = make_shared(nullptr, false); + auto tableFactory2 = make_shared(memoryStorage2, false); for (int i = 10; i < 20; ++i) { @@ -568,7 +568,7 @@ BOOST_AUTO_TEST_CASE(chainLink) StateStorage::Ptr prev = nullptr; for (int i = 0; i < 10; ++i) { - auto tableStorage = std::make_shared(prev); + auto tableStorage = std::make_shared(prev, false); for (int j = 0; j < 10; ++j) { auto tableName = "table_" + boost::lexical_cast(i) + "_" + @@ -726,8 +726,8 @@ BOOST_AUTO_TEST_CASE(getRows) auto valueFields = "value1,value2,value3"; StateStorage::Ptr prev = nullptr; - prev = std::make_shared(prev); - auto tableStorage = std::make_shared(prev); + prev = std::make_shared(prev, false); + auto tableStorage = std::make_shared(prev, false); BOOST_CHECK(prev->createTable("t_test", valueFields)); @@ -886,7 +886,7 @@ BOOST_AUTO_TEST_CASE(checkVersion) BOOST_AUTO_TEST_CASE(deleteAndGetRows) { - StateStorage::Ptr storage1 = std::make_shared(nullptr); + StateStorage::Ptr storage1 = std::make_shared(nullptr, false); storage1->setEnableTraverse(true); storage1->asyncCreateTable( @@ -905,14 +905,14 @@ BOOST_AUTO_TEST_CASE(deleteAndGetRows) storage1->asyncSetRow( "table", "key2", std::move(entry2), [](Error::UniquePtr error) { BOOST_CHECK(!error); }); - StateStorage::Ptr storage2 = std::make_shared(storage1); + StateStorage::Ptr storage2 = std::make_shared(storage1, false); storage2->setEnableTraverse(true); Entry deleteEntry; deleteEntry.setStatus(Entry::DELETED); storage2->asyncSetRow("table", "key2", std::move(deleteEntry), [](Error::UniquePtr error) { BOOST_CHECK(!error); }); - StateStorage::Ptr storage3 = std::make_shared(storage2); + StateStorage::Ptr storage3 = std::make_shared(storage2, false); storage3->asyncGetPrimaryKeys( "table", std::nullopt, [](Error::UniquePtr error, std::vector keys) { BOOST_CHECK(!error); @@ -923,7 +923,7 @@ BOOST_AUTO_TEST_CASE(deleteAndGetRows) BOOST_AUTO_TEST_CASE(deletedAndGetRow) { - StateStorage::Ptr storage1 = std::make_shared(nullptr); + StateStorage::Ptr storage1 = std::make_shared(nullptr, false); storage1->asyncCreateTable( "table", "value", [](Error::UniquePtr error, std::optional
table) { @@ -936,7 +936,7 @@ BOOST_AUTO_TEST_CASE(deletedAndGetRow) storage1->asyncSetRow( "table", "key1", std::move(entry1), [](Error::UniquePtr error) { BOOST_CHECK(!error); }); - StateStorage::Ptr storage2 = std::make_shared(storage1); + StateStorage::Ptr storage2 = std::make_shared(storage1, false); Entry deleteEntry; deleteEntry.setStatus(Entry::DELETED); storage2->asyncSetRow("table", "key1", std::move(deleteEntry), @@ -955,7 +955,7 @@ BOOST_AUTO_TEST_CASE(deletedAndGetRow) BOOST_AUTO_TEST_CASE(deletedAndGetRows) { - StateStorage::Ptr storage1 = std::make_shared(nullptr); + StateStorage::Ptr storage1 = std::make_shared(nullptr, false); storage1->asyncCreateTable( "table", "value", [](Error::UniquePtr error, std::optional
table) { @@ -968,7 +968,7 @@ BOOST_AUTO_TEST_CASE(deletedAndGetRows) storage1->asyncSetRow( "table", "key1", std::move(entry1), [](Error::UniquePtr error) { BOOST_CHECK(!error); }); - StateStorage::Ptr storage2 = std::make_shared(storage1); + StateStorage::Ptr storage2 = std::make_shared(storage1, false); Entry deleteEntry; deleteEntry.setStatus(Entry::DELETED); storage2->asyncSetRow("table", "key1", std::move(deleteEntry), @@ -985,7 +985,7 @@ BOOST_AUTO_TEST_CASE(deletedAndGetRows) BOOST_AUTO_TEST_CASE(rollbackAndGetRow) { - StateStorage::Ptr storage1 = std::make_shared(nullptr); + StateStorage::Ptr storage1 = std::make_shared(nullptr, false); storage1->asyncCreateTable( "table", "value", [](Error::UniquePtr error, std::optional
table) { @@ -998,7 +998,7 @@ BOOST_AUTO_TEST_CASE(rollbackAndGetRow) storage1->asyncSetRow( "table", "key1", std::move(entry1), [](Error::UniquePtr error) { BOOST_CHECK(!error); }); - StateStorage::Ptr storage2 = std::make_shared(storage1); + StateStorage::Ptr storage2 = std::make_shared(storage1, false); auto recoder = std::make_shared(); storage2->setRecoder(recoder); @@ -1024,7 +1024,7 @@ BOOST_AUTO_TEST_CASE(rollbackAndGetRow) BOOST_AUTO_TEST_CASE(rollbackAndGetRows) { - StateStorage::Ptr storage1 = std::make_shared(nullptr); + StateStorage::Ptr storage1 = std::make_shared(nullptr, false); storage1->asyncCreateTable( "table", "value", [](Error::UniquePtr error, std::optional
table) { @@ -1037,7 +1037,7 @@ BOOST_AUTO_TEST_CASE(rollbackAndGetRows) storage1->asyncSetRow( "table", "key1", std::move(entry1), [](Error::UniquePtr error) { BOOST_CHECK(!error); }); - StateStorage::Ptr storage2 = std::make_shared(storage1); + StateStorage::Ptr storage2 = std::make_shared(storage1, false); auto recoder = std::make_shared(); storage2->setRecoder(recoder); @@ -1101,7 +1101,7 @@ BOOST_AUTO_TEST_CASE(randomRWHash) StateStorage::Ptr prev; for (size_t i = 0; i < 10; ++i) { - StateStorage::Ptr storage = std::make_shared(prev); + StateStorage::Ptr storage = std::make_shared(prev, false); for (auto& it : rwSet) { diff --git a/tests/perf/benchmark.cpp b/tests/perf/benchmark.cpp index ecab3be426..506a84ebee 100644 --- a/tests/perf/benchmark.cpp +++ b/tests/perf/benchmark.cpp @@ -119,7 +119,7 @@ int main(int argc, const char* argv[]) } else { - storage = std::make_shared(rocksDBStorage); + storage = std::make_shared(nullptr, false); } std::vector storages; // create Table @@ -150,7 +150,7 @@ int main(int argc, const char* argv[]) } else { - storage = std::make_shared(storage); + storage = std::make_shared(nullptr, false); } table = storage->openTable(testTableName).value(); } @@ -210,7 +210,7 @@ int main(int argc, const char* argv[]) } else { - storage = std::make_shared(rocksDBStorage); + storage = std::make_shared(nullptr, false); } auto prepareCleanStorageEnd = std::chrono::system_clock::now(); @@ -281,7 +281,7 @@ int main(int argc, const char* argv[]) } else { - storage = std::make_shared(rocksDBStorage); + storage = std::make_shared(nullptr, false); } table = storage->createTable(testTableName, "value"); if (!table) @@ -318,7 +318,7 @@ int main(int argc, const char* argv[]) } else { - storage = std::make_shared(storage); + storage = std::make_shared(nullptr, false); } table = storage->openTable(testTableName).value(); } From b72d9d416662f2609b94bb71f6bda7bd1f6fc93e Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Fri, 10 May 2024 14:15:02 +0800 Subject: [PATCH 20/39] (rpc): fix web3 rpc bug in integration testing with hardhat. (#4444) --- bcos-codec/bcos-codec/rlp/Common.h | 26 +++ bcos-crypto/bcos-crypto/ChecksumAddress.h | 14 ++ .../signature/secp256k1/Secp256k1Crypto.h | 1 + .../src/executor/TransactionExecutor.cpp | 1 + bcos-rpc/bcos-rpc/filter/Filter.h | 2 +- .../bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp | 2 +- .../web3jsonrpc/endpoints/EthEndpoint.cpp | 201 +++++++++++++----- .../web3jsonrpc/model/BlockResponse.h | 26 ++- .../web3jsonrpc/model/CallRequest.cpp | 5 + .../web3jsonrpc/model/ReceiptResponse.h | 13 +- .../web3jsonrpc/model/TransactionResponse.h | 19 +- .../web3jsonrpc/model/Web3FilterRequest.h | 10 +- .../web3jsonrpc/model/Web3Transaction.cpp | 17 +- .../web3jsonrpc/model/Web3Transaction.h | 8 +- bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.cpp | 12 ++ bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.h | 3 + bcos-rpc/test/unittests/rpc/BloomTest.cpp | 79 +++++++ bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp | 49 ++--- bcos-scheduler/test/testChecksumAddress.cpp | 78 ++++++- .../bcos-tars-protocol/Common.h | 16 ++ .../protocol/TransactionReceiptImpl.cpp | 12 +- bcos-utilities/bcos-utilities/FixedBytes.h | 2 +- bcos-utilities/testutils/TestPromptFixture.h | 9 + 23 files changed, 475 insertions(+), 130 deletions(-) create mode 100644 bcos-rpc/test/unittests/rpc/BloomTest.cpp diff --git a/bcos-codec/bcos-codec/rlp/Common.h b/bcos-codec/bcos-codec/rlp/Common.h index 7482bb6d7c..4d899cde54 100644 --- a/bcos-codec/bcos-codec/rlp/Common.h +++ b/bcos-codec/bcos-codec/rlp/Common.h @@ -109,6 +109,32 @@ inline size_t length(std::unsigned_integral auto n) noexcept } } +template +inline size_t length(T const& n) noexcept + requires(std::convertible_to && !std::integral) +{ + if (n < BYTES_HEAD_BASE) + { + return 1; + } + size_t word = n.backend().internal_limb_count; + for (; word > 0; --word) + { + if (n.backend().limbs()[word - 1] != 0) + { + break; + } + } + if (word != 0) + { + auto significantBytes = + (64u - std::countl_zero(n.backend().limbs()[word - 1]) + 7) / 8u + (word - 1) * 8; + const size_t n_bytes{significantBytes}; + return n_bytes + lengthOfLength(n_bytes); + } + return 1; +} + inline size_t length(bool) noexcept { return 1; diff --git a/bcos-crypto/bcos-crypto/ChecksumAddress.h b/bcos-crypto/bcos-crypto/ChecksumAddress.h index 2adb2fd3ff..8bd8cc7655 100644 --- a/bcos-crypto/bcos-crypto/ChecksumAddress.h +++ b/bcos-crypto/bcos-crypto/ChecksumAddress.h @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -65,6 +66,19 @@ inline void toCheckSumAddress(std::string& _hexAddress, crypto::Hash::Ptr _hashI toChecksumAddress(_hexAddress, _hashImpl->hash(_hexAddress).hex()); } +// for EIP-1191, hexAdress input should NOT have prefix "0x" +inline void toCheckSumAddressWithChainId( + std::string& _hexAddress, crypto::Hash::Ptr _hashImpl, uint64_t _chainId = 0) +{ + boost::algorithm::to_lower(_hexAddress); + std::string hashInput = _hexAddress; + if (_chainId != 0 && _chainId != 1) + { + hashInput = fmt::format("{}0x{}", _chainId, _hexAddress); + } + toChecksumAddress(_hexAddress, _hashImpl->hash(hashInput).hex()); +} + inline void toAddress(std::string& _hexAddress, [[maybe_unused]] crypto::Hash::Ptr _hashImpl) { boost::algorithm::to_lower(_hexAddress); diff --git a/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1Crypto.h b/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1Crypto.h index c2c209e2e3..c413ee1eb5 100644 --- a/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1Crypto.h +++ b/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1Crypto.h @@ -24,6 +24,7 @@ namespace bcos::crypto { +constexpr uint16_t SECP256K1_SIGNATURE_R_LEN = 32; const int SECP256K1_SIGNATURE_LEN = 65; const int SECP256K1_UNCOMPRESS_PUBLICKEY_LEN = 65; const int SECP256K1_PUBLICKEY_LEN = 64; diff --git a/bcos-executor/src/executor/TransactionExecutor.cpp b/bcos-executor/src/executor/TransactionExecutor.cpp index 0158f2532b..79db552be2 100644 --- a/bcos-executor/src/executor/TransactionExecutor.cpp +++ b/bcos-executor/src/executor/TransactionExecutor.cpp @@ -2710,6 +2710,7 @@ std::unique_ptr TransactionExecutor::createCallParameters( 0, addressSize - callParameters->receiveAddress.size(), '0'); } } + // FIXME)): if input is hex without prefix, will throw exception and abort coredump callParameters->value = u256(input.value()); callParameters->gasPrice = u256(input.gasPrice()); callParameters->gasLimit = input.gasLimit(); diff --git a/bcos-rpc/bcos-rpc/filter/Filter.h b/bcos-rpc/bcos-rpc/filter/Filter.h index ba3e622b95..71c2136099 100644 --- a/bcos-rpc/bcos-rpc/filter/Filter.h +++ b/bcos-rpc/bcos-rpc/filter/Filter.h @@ -24,7 +24,7 @@ class Filter : m_fullTx(fullTx), m_type(type), m_id(id), - m_params(params), + m_params(params), m_startBlockNumber(startBlockNumber), m_lastAccessTime(utcTime()), m_group(group) diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp index cadde5722a..f0e76452b4 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.cpp @@ -48,7 +48,7 @@ void Web3JsonRpcImpl::onRPCRequest(std::string_view _requestBody, Sender _sender Json::Value resp; try { - // FIXME: throw exception here will core dump + // FIXME)): throw exception here will core dump Json::Value const& params = _request["params"]; co_await (self->m_endpoints.*_handler)(params, resp); diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp index 9246516e86..7249453b7d 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp @@ -170,21 +170,30 @@ task::Task EthEndpoint::getStorageAt(const Json::Value& request, Json::Val } std::string addressStr(address); boost::algorithm::to_lower(addressStr); - auto const position = toView(request[1u]); + auto position = toView(request[1u]); + std::string positionStr = + std::string(position.starts_with("0x") ? position.substr(2) : position); + if (position.size() % 2 != 0) + { + positionStr.insert(0, "0"); + } + const auto posistionBytes = FixedBytes<32>(positionStr, FixedBytes<32>::FromHex); // TODO)): blockNumber is ignored nowadays // auto const blockTag = toView(request[2u]); // auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); auto const ledger = m_nodeService->ledger(); Json::Value result; - if (auto const entry = - co_await ledger::getStorageAt(*ledger, addressStr, position, /*blockNumber*/ 0); + if (auto const entry = co_await ledger::getStorageAt( + *ledger, addressStr, posistionBytes.toRawString(), /*blockNumber*/ 0); entry.has_value()) { - result = std::string(entry.value().get()); + auto const value = entry.value().get(); + result = toHex(value, "0x"); } else { - BOOST_THROW_EXCEPTION(JsonRpcException(JsonRpcError::InvalidParams, "Invalid params!")); + // empty value + result = "0x0000000000000000000000000000000000000000000000000000000000000000"; } buildJsonContent(result, response); co_return; @@ -237,9 +246,18 @@ task::Task EthEndpoint::getBlockTxCountByHash( auto const hashStr = toView(request[0U]); auto hash = crypto::HashType(hashStr, crypto::HashType::FromHex); auto const ledger = m_nodeService->ledger(); - auto number = co_await ledger::getBlockNumber(*ledger, std::move(hash)); - auto block = co_await ledger::getBlockData(*ledger, number, bcos::ledger::TRANSACTIONS_HASH); - Json::Value result = toQuantity(block->transactionsHashSize()); + Json::Value result; + try + { + auto number = co_await ledger::getBlockNumber(*ledger, std::move(hash)); + auto block = + co_await ledger::getBlockData(*ledger, number, bcos::ledger::TRANSACTIONS_HASH); + result = toQuantity(block->transactionsHashSize()); + } + catch (...) + { + result = "0x0"; + } buildJsonContent(result, response); co_return; } @@ -250,8 +268,17 @@ task::Task EthEndpoint::getBlockTxCountByNumber( // result: transactionCount(QTY) auto const number = fromQuantity(std::string(toView(request[0U]))); auto const ledger = m_nodeService->ledger(); - auto block = co_await ledger::getBlockData(*ledger, number, bcos::ledger::TRANSACTIONS_HASH); - Json::Value result = toQuantity(block->transactionsHashSize()); + Json::Value result; + try + { + auto const block = + co_await ledger::getBlockData(*ledger, number, bcos::ledger::TRANSACTIONS_HASH); + result = toQuantity(block->transactionsHashSize()); + } + catch (...) + { + result = "0x0"; + } buildJsonContent(result, response); co_return; } @@ -323,7 +350,7 @@ task::Task EthEndpoint::sign(const Json::Value&, Json::Value& response) { // params: address(DATA), message(DATA) // result: signature(DATA) - Json::Value result = "0x0"; + Json::Value result = "0x00"; buildJsonContent(result, response); co_return; } @@ -331,7 +358,7 @@ task::Task EthEndpoint::signTransaction(const Json::Value&, Json::Value& r { // params: transaction(TX), address(DATA) // result: signedTransaction(DATA) - Json::Value result = "0x0"; + Json::Value result = "0x00"; buildJsonContent(result, response); co_return; } @@ -339,7 +366,7 @@ task::Task EthEndpoint::sendTransaction(const Json::Value&, Json::Value& r { // params: transaction(TX) // result: transactionHash(DATA) - Json::Value result = "0x0"; + Json::Value result = "0x0000000000000000000000000000000000000000000000000000000000000000"; buildJsonContent(result, response); co_return; } @@ -374,20 +401,32 @@ task::Task EthEndpoint::sendRawTransaction(const Json::Value& request, Jso WEB3_LOG(TRACE) << LOG_DESC("sendRawTransaction") << web3Tx.toString(); } txpool->broadcastTransaction(*tx); - auto const txResult = co_await txpool->submitTransactionWithoutReceipt(std::move(tx)); - crypto::HashType hash{}; + auto const txResult = co_await txpool->submitTransaction(std::move(tx)); + auto const hash = std::move(web3TxHash); if (txResult->status() == 0) { - hash = std::move(web3TxHash); + Json::Value result = hash.hexPrefixed(); + buildJsonContent(result, response); } - if (c_fileLogLevel == TRACE) + else + { + protocol::TransactionStatus status = + static_cast(txResult->status()); + Json::Value errorData = Json::objectValue; + errorData["txHash"] = hash.hexPrefixed(); + auto output = toHex(txResult->transactionReceipt()->output(), "0x"); + auto msg = fmt::format("VM Exception while processing transaction, reason: {}, msg: {}", + protocol::toString(status), output); + errorData["message"] = msg; + errorData["data"] = std::move(output); + buildJsonErrorWithData(errorData, InternalError, std::move(msg), response); + } + if (c_fileLogLevel == TRACE) [[unlikely]] { WEB3_LOG(TRACE) << LOG_DESC("sendRawTransaction finished") << LOG_KV("status", txResult->status()) - << LOG_KV("hash", hash.hexPrefixed()); + << LOG_KV("hash", hash.hexPrefixed()) << LOG_KV("rsp", printJson(response)); } - Json::Value result = hash.hexPrefixed(); - buildJsonContent(result, response); co_return; } task::Task EthEndpoint::call(const Json::Value& request, Json::Value& response) @@ -477,11 +516,20 @@ task::Task EthEndpoint::getBlockByHash(const Json::Value& request, Json::V auto const blockHash = toView(request[0u]); auto const fullTransaction = request[1u].asBool(); auto const ledger = m_nodeService->ledger(); - auto const number = co_await ledger::getBlockNumber( - *ledger, crypto::HashType(blockHash, crypto::HashType::FromHex)); - auto block = co_await ledger::getBlockData(*ledger, number, bcos::ledger::FULL_BLOCK); Json::Value result = Json::objectValue; - combineBlockResponse(result, std::move(block), fullTransaction); + try + { + auto const number = co_await ledger::getBlockNumber( + *ledger, crypto::HashType(blockHash, crypto::HashType::FromHex)); + auto flag = bcos::ledger::HEADER; + flag |= fullTransaction ? bcos::ledger::TRANSACTIONS : bcos::ledger::TRANSACTIONS_HASH; + auto block = co_await ledger::getBlockData(*ledger, number, flag); + combineBlockResponse(result, std::move(block), fullTransaction); + } + catch (...) + { + result = Json::nullValue; + } buildJsonContent(result, response); co_return; } @@ -491,11 +539,18 @@ task::Task EthEndpoint::getBlockByNumber(const Json::Value& request, Json: // result: block(BLOCK) auto const blockTag = toView(request[0u]); auto const fullTransaction = request[1u].asBool(); - auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); - auto const ledger = m_nodeService->ledger(); - auto block = co_await ledger::getBlockData(*ledger, blockNumber, bcos::ledger::FULL_BLOCK); Json::Value result = Json::objectValue; - combineBlockResponse(result, std::move(block), fullTransaction); + try + { + auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); + auto const ledger = m_nodeService->ledger(); + auto block = co_await ledger::getBlockData(*ledger, blockNumber, bcos::ledger::FULL_BLOCK); + combineBlockResponse(result, std::move(block), fullTransaction); + } + catch (...) + { + result = Json::nullValue; + } buildJsonContent(result, response); co_return; } @@ -509,17 +564,25 @@ task::Task EthEndpoint::getTransactionByHash( auto hashList = std::make_shared(); hashList->push_back(hash); auto const ledger = m_nodeService->ledger(); - auto const txs = co_await ledger::getTransactions(*ledger, std::move(hashList)); - auto receipt = co_await ledger::getReceipt(*ledger, hash); - if (!receipt || !txs || txs->empty()) + Json::Value result = Json::objectValue; + try { - BOOST_THROW_EXCEPTION( - JsonRpcException(InvalidParams, "Invalid transaction hash: " + hash.hexPrefixed())); + auto const txs = co_await ledger::getTransactions(*ledger, std::move(hashList)); + auto receipt = co_await ledger::getReceipt(*ledger, hash); + if (!receipt || !txs || txs->empty()) + { + result = Json::nullValue; + buildJsonContent(result, response); + co_return; + } + auto block = co_await ledger::getBlockData(*ledger, receipt->blockNumber(), + bcos::ledger::HEADER | bcos::ledger::TRANSACTIONS_HASH); + combineTxResponse(result, txs->at(0), std::move(receipt), std::move(block)); + } + catch (...) + { + result = Json::nullValue; } - auto block = co_await ledger::getBlockData( - *ledger, receipt->blockNumber(), bcos::ledger::HEADER | bcos::ledger::TRANSACTIONS_HASH); - Json::Value result = Json::objectValue; - combineTxResponse(result, txs->at(0), std::move(receipt), std::move(block)); buildJsonContent(result, response); co_return; } @@ -532,20 +595,24 @@ task::Task EthEndpoint::getTransactionByBlockHashAndIndex( auto const transactionIndex = fromQuantity(std::string(toView(request[1u]))); auto const hash = crypto::HashType(blockHash, crypto::HashType::FromHex); auto const ledger = m_nodeService->ledger(); + Json::Value result = Json::objectValue; auto const number = co_await ledger::getBlockNumber(*ledger, hash); + // will not throw exception in getBlockNumber if not found if (number <= 0) [[unlikely]] { - BOOST_THROW_EXCEPTION( - JsonRpcException(InvalidParams, "Invalid block hash: " + hash.hexPrefixed())); + result = Json::nullValue; + buildJsonContent(result, response); + co_return; } auto block = co_await ledger::getBlockData( *ledger, number, bcos::ledger::TRANSACTIONS | bcos::ledger::HEADER); if (!block || transactionIndex >= block->transactionsSize()) [[unlikely]] { - BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParams, "Invalid transaction index!")); + result = Json::nullValue; + buildJsonContent(result, response); + co_return; } auto tx = block->transaction(transactionIndex); - Json::Value result = Json::objectValue; combineTxResponse(result, std::move(tx), nullptr, std::move(block)); buildJsonContent(result, response); co_return; @@ -559,15 +626,22 @@ task::Task EthEndpoint::getTransactionByBlockNumberAndIndex( auto const transactionIndex = fromQuantity(std::string(toView(request[1u]))); auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); auto const ledger = m_nodeService->ledger(); - auto block = co_await ledger::getBlockData( - *ledger, blockNumber, bcos::ledger::TRANSACTIONS | bcos::ledger::HEADER); - if (!block || transactionIndex >= block->transactionsSize()) [[unlikely]] + Json::Value result = Json::objectValue; + try { - BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParams, "Invalid transaction index!")); + auto block = co_await ledger::getBlockData( + *ledger, blockNumber, bcos::ledger::TRANSACTIONS | bcos::ledger::HEADER); + if (!block || transactionIndex >= block->transactionsSize()) [[unlikely]] + { + BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParams, "Invalid transaction index!")); + } + auto tx = block->transaction(transactionIndex); + combineTxResponse(result, std::move(tx), nullptr, std::move(block)); + } + catch (...) + { + result = Json::nullValue; } - auto tx = block->transaction(transactionIndex); - Json::Value result = Json::objectValue; - combineTxResponse(result, std::move(tx), nullptr, std::move(block)); buildJsonContent(result, response); co_return; } @@ -579,19 +653,28 @@ task::Task EthEndpoint::getTransactionReceipt( auto const hashStr = toView(request[0U]); auto const hash = crypto::HashType(hashStr, crypto::HashType::FromHex); auto const ledger = m_nodeService->ledger(); - auto receipt = co_await ledger::getReceipt(*ledger, hash); - auto hashList = std::make_shared(); - hashList->push_back(hash); - auto txs = co_await ledger::getTransactions(*ledger, std::move(hashList)); - if (!receipt || !txs || txs->empty()) + Json::Value result = Json::objectValue; + try { - BOOST_THROW_EXCEPTION( - JsonRpcException(InvalidParams, "Invalid transaction hash: " + hash.hexPrefixed())); + auto receipt = co_await ledger::getReceipt(*ledger, hash); + auto hashList = std::make_shared(); + hashList->push_back(hash); + auto txs = co_await ledger::getTransactions(*ledger, std::move(hashList)); + if (!receipt || !txs || txs->empty()) + { + BOOST_THROW_EXCEPTION( + JsonRpcException(InvalidParams, "Invalid transaction hash: " + hash.hexPrefixed())); + } + auto block = co_await ledger::getBlockData(*ledger, receipt->blockNumber(), + bcos::ledger::HEADER | bcos::ledger::TRANSACTIONS_HASH); + combineReceiptResponse(result, std::move(receipt), txs->at(0), std::move(block)); + } + catch (...) + { + result = Json::nullValue; + buildJsonContent(result, response); + co_return; } - auto block = co_await ledger::getBlockData( - *ledger, receipt->blockNumber(), bcos::ledger::HEADER | bcos::ledger::TRANSACTIONS_HASH); - Json::Value result = Json::objectValue; - combineReceiptResponse(result, std::move(receipt), txs->at(0), std::move(block)); buildJsonContent(result, response); co_return; } diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.h index 939add14d7..82e4ad0fcf 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.h @@ -21,6 +21,7 @@ #pragma once #include #include +#include #include #include #include @@ -50,10 +51,27 @@ namespace bcos::rpc result["gasLimit"] = toQuantity(3000000000ull); result["gasUsed"] = toQuantity((uint64_t)block->blockHeader()->gasUsed()); result["timestamp"] = toQuantity(block->blockHeader()->timestamp()); - // if (fullTxs) - // { - // result["transactions"] = Json::Value(Json::arrayValue); - // } + if (fullTxs) + { + Json::Value txList = Json::arrayValue; + for (size_t i = 0; i < block->transactionsSize(); i++) + { + Json::Value txJson; + auto tx = block->transaction(i); + combineTxResponse(txJson, std::move(tx), nullptr, block); + txList.append(txJson); + } + result["transactions"] = std::move(txList); + } + else + { + Json::Value txHashesList = Json::arrayValue; + for (size_t i = 0; i < block->transactionsHashSize(); i++) + { + txHashesList.append(block->transactionHash(i).hexPrefixed()); + } + result["transactions"] = std::move(txHashesList); + } result["uncles"] = Json::Value(Json::arrayValue); } } // namespace bcos::rpc \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/CallRequest.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/model/CallRequest.cpp index 977d94f09f..5626624b86 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/CallRequest.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/CallRequest.cpp @@ -29,6 +29,11 @@ bcos::protocol::Transaction::Ptr CallRequest::takeToTransaction( auto tx = factory->createTransaction(0, std::move(this->to), std::move(this->data), "", 0, {}, {}, 0, "", value.value_or(""), gasPrice.value_or(""), gas.value_or(0), maxFeePerGas.value_or(""), maxPriorityFeePerGas.value_or("")); + if (from.has_value()) + { + auto sender = fromHexWithPrefix(from.value()); + tx->forceSender(std::move(sender)); + } return tx; } diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h index e34cc067d2..e7904a166f 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h @@ -63,16 +63,17 @@ namespace bcos::rpc result["transactionIndex"] = toQuantity(transactionIndex); result["blockHash"] = blockHash.hexPrefixed(); result["blockNumber"] = toQuantity(blockNumber); - auto from = toHexStringWithPrefix(tx->sender()); - toChecksumAddress(from, bcos::crypto::keccak256Hash(from).hexPrefixed(), "0x"); - result["from"] = std::move(from); + auto from = toHex(tx->sender()); + toChecksumAddress(from, bcos::crypto::keccak256Hash(from).hex()); + result["from"] = "0x" + std::move(from); if (tx->to().empty()) { - result["to"] = ""; + result["to"] = Json::nullValue; } else { - auto to = std::string(tx->to()); + auto toView = tx->to(); + auto to = std::string(toView.starts_with("0x") ? toView.substr(2) : toView); toChecksumAddress(to, bcos::crypto::keccak256Hash(to).hex()); result["to"] = "0x" + std::move(to); } @@ -109,7 +110,7 @@ namespace bcos::rpc log["blockNumber"] = toQuantity(blockNumber); log["blockHash"] = blockHash.hexPrefixed(); log["transactionIndex"] = toQuantity(transactionIndex); - log["transactionHash"] = receipt->hash().hexPrefixed(); + log["transactionHash"] = tx->hash().hexPrefixed(); log["removed"] = false; result["logs"].append(std::move(log)); } diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h index 8e5c345fda..26e125fb86 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h @@ -19,6 +19,10 @@ */ #pragma once +#include "Web3Transaction.h" + + +#include #include #include #include @@ -30,7 +34,7 @@ namespace bcos::rpc { // block and receipt are nullable static void combineTxResponse(Json::Value& result, bcos::protocol::Transaction::ConstPtr&& tx, - protocol::TransactionReceipt::ConstPtr&& receipt, bcos::protocol::Block::Ptr&& block) + protocol::TransactionReceipt::ConstPtr&& receipt, bcos::protocol::Block::Ptr const& block) { if (!result.isObject()) { @@ -54,21 +58,24 @@ static void combineTxResponse(Json::Value& result, bcos::protocol::Transaction:: result["blockHash"] = blockHash.hexPrefixed(); result["blockNumber"] = toQuantity(blockNumber); result["transactionIndex"] = toQuantity(transactionIndex); - auto from = toHexStringWithPrefix(tx->sender()); - toChecksumAddress(from, bcos::crypto::keccak256Hash(from).hexPrefixed(), "0x"); - result["from"] = std::move(from); + auto from = toHex(tx->sender()); + toChecksumAddress(from, bcos::crypto::keccak256Hash(from).hex()); + result["from"] = "0x" + std::move(from); if (tx->to().empty()) { result["to"] = Json::nullValue; } else { - auto to = std::string(tx->to()); + auto toView = tx->to(); + auto to = std::string(toView.starts_with("0x") ? toView.substr(2) : toView); toChecksumAddress(to, bcos::crypto::keccak256Hash(to).hex()); result["to"] = "0x" + std::move(to); } result["gas"] = toQuantity(tx->gasLimit()); - result["gasPrice"] = std::string(receipt ? receipt->effectiveGasPrice() : tx->gasPrice()); + auto const gasPrice = receipt ? receipt->effectiveGasPrice() : tx->gasPrice(); + // FIXME)): return will case coredump in executor + result["gasPrice"] = std::string(gasPrice.empty() ? "20200" /*21000*/ : gasPrice); result["hash"] = tx->hash().hexPrefixed(); result["input"] = toHexStringWithPrefix(tx->input()); diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3FilterRequest.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3FilterRequest.h index d73ad29d96..bcda0a88f0 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3FilterRequest.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3FilterRequest.h @@ -31,10 +31,7 @@ class Web3FilterRequest : public FilterRequest Web3FilterRequest() = default; ~Web3FilterRequest() = default; Web3FilterRequest(const FilterRequest& oth) : FilterRequest(oth) {} - virtual int32_t InvalidParamsCode() override - { - return Web3JsonRpcError::Web3DefautError; - } + virtual int32_t InvalidParamsCode() override { return Web3JsonRpcError::Web3DefautError; } }; class Web3FilterRequestFactory : public FilterRequestFactory @@ -42,10 +39,7 @@ class Web3FilterRequestFactory : public FilterRequestFactory public: Web3FilterRequestFactory() = default; ~Web3FilterRequestFactory() = default; - FilterRequest::Ptr create() override - { - return std::make_shared(); - } + FilterRequest::Ptr create() override { return std::make_shared(); } FilterRequest::Ptr create(const FilterRequest& req) override { return std::make_shared(req); diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp index f41042ef24..38592d79cf 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp @@ -114,16 +114,18 @@ bcostars::Transaction Web3Transaction::takeToTarsTransaction() tarsTx.data.to = (this->to == Address()) ? "" : this->to.hexPrefixed(); tarsTx.data.input.reserve(this->data.size()); RANGES::move(this->data, std::back_inserter(tarsTx.data.input)); - tarsTx.data.value = toHex(this->value); + + tarsTx.data.value = "0x" + this->value.str(0, std::ios_base::hex); tarsTx.data.gasLimit = this->gasLimit; if (static_cast(this->type) >= static_cast(TransactionType::EIP1559)) { - tarsTx.data.maxFeePerGas = toHex(this->maxFeePerGas); - tarsTx.data.maxPriorityFeePerGas = toHex(this->maxPriorityFeePerGas); + tarsTx.data.maxFeePerGas = "0x" + this->maxFeePerGas.str(0, std::ios_base::hex); + tarsTx.data.maxPriorityFeePerGas = + "0x" + this->maxPriorityFeePerGas.str(0, std::ios_base::hex); } else { - tarsTx.data.gasPrice = toHex(this->maxPriorityFeePerGas); + tarsTx.data.gasPrice = "0x" + this->maxPriorityFeePerGas.str(0, std::ios_base::hex); } tarsTx.type = static_cast(bcos::protocol::TransactionType::Web3Transacion); auto hashForSign = this->hashForSign(); @@ -143,6 +145,7 @@ bcostars::Transaction Web3Transaction::takeToTarsTransaction() reinterpret_cast(tarsTx.signature.data()), tarsTx.signature.size()); auto [_, sender] = signatureImpl.recoverAddress(hashImpl, hashForSign, signRef); tarsTx.data.nonce = toHexStringWithPrefix(sender) + toQuantity(this->nonce); + tarsTx.data.chainID = std::to_string(this->chainId.value_or(0)); tarsTx.dataHash.reserve(crypto::HashType::SIZE); RANGES::move(hashForSign, std::back_inserter(tarsTx.dataHash)); tarsTx.sender.reserve(sender.size()); @@ -364,11 +367,13 @@ bcos::Error::UniquePtr decode(bcos::bytesRef& in, Web3Transaction& out) noexcept out.value, out.data, out.signatureV, out.signatureR, out.signatureS); if (out.signatureR.size() < crypto::SECP256K1_SIGNATURE_LEN / 2) { - out.signatureR.insert(out.signatureR.begin(), bcos::byte(0)); + out.signatureR.insert( + out.signatureR.begin(), crypto::SECP256K1_SIGNATURE_R_LEN - out.signatureR.size(), 0); } if (out.signatureS.size() < crypto::SECP256K1_SIGNATURE_LEN / 2) { - out.signatureS.insert(out.signatureS.begin(), bcos::byte(0)); + out.signatureS.insert(out.signatureS.begin(), + crypto::SECP256K1_SIGNATURE_R_LEN - out.signatureS.size(), bcos::byte(0)); } // TODO: EIP-155 chainId decode from encoded bytes for sign out.maxFeePerGas = out.maxPriorityFeePerGas; diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h index 56ab243707..c9c2054895 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h @@ -128,16 +128,16 @@ class Web3Transaction TransactionType type{TransactionType::Legacy}; Address to{}; bcos::bytes data{}; - uint64_t value{0}; + u256 value{0}; uint64_t nonce{0}; uint64_t gasLimit{0}; // EIP-2930: Optional access lists std::vector accessList{}; // EIP-1559: Fee market change for ETH 1.0 chain - uint64_t maxPriorityFeePerGas{0}; // for legacy tx, it stands for gasPrice - uint64_t maxFeePerGas{0}; + u256 maxPriorityFeePerGas{0}; // for legacy tx, it stands for gasPrice + u256 maxFeePerGas{0}; // EIP-4844: Shard Blob Transactions - uint64_t maxFeePerBlobGas{0}; + u256 maxFeePerBlobGas{0}; h256s blobVersionedHashes{}; // TODO)) blob bcos::bytes signatureR{}; diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.cpp index 2ba17b26aa..8c187a60e6 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.cpp @@ -39,3 +39,15 @@ void bcos::rpc::buildJsonError( error["message"] = std::move(message); response["error"] = std::move(error); } + +void bcos::rpc::buildJsonErrorWithData( + Json::Value& data, int32_t code, std::string message, Json::Value& response) +{ + response["jsonrpc"] = "2.0"; + // maybe request not init + Json::Value error = Json::objectValue; + error["code"] = code; + error["message"] = std::move(message); + error["data"].swap(data); + response["error"] = std::move(error); +} diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.h b/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.h index 6a0f3dc802..d51d847466 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/utils/util.h @@ -20,6 +20,7 @@ #pragma once #include +#include #include namespace bcos::rpc @@ -27,6 +28,8 @@ namespace bcos::rpc void buildJsonContent(Json::Value& result, Json::Value& response); void buildJsonError( Json::Value const& request, int32_t code, std::string message, Json::Value& response); +void buildJsonErrorWithData( + Json::Value& data, int32_t code, std::string message, Json::Value& response); inline auto printJson(const Json::Value& value) { Json::StreamWriterBuilder builder; diff --git a/bcos-rpc/test/unittests/rpc/BloomTest.cpp b/bcos-rpc/test/unittests/rpc/BloomTest.cpp new file mode 100644 index 0000000000..4faa47b49c --- /dev/null +++ b/bcos-rpc/test/unittests/rpc/BloomTest.cpp @@ -0,0 +1,79 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file BloomTest.cpp + * @author: kyonGuo + * @date 2024/4/29 + */ + + +#include "../common/RPCFixture.h" +#include +#include +#include +#include +#include +#include +#include +using namespace bcos; +using namespace bcos::rpc; +using namespace bcos::codec::rlp; +using namespace std::literals; +using namespace std::string_view_literals; +namespace bcos::test +{ +BOOST_FIXTURE_TEST_SUITE(testBloom, RPCFixture) +BOOST_AUTO_TEST_CASE(testLogsToBloom) +{ + // case silkworm + { + Logs logs{}; + logs.push_back({.address = 0x22341ae42d6dd7384bc8584e50419ea3ac75b83f_bytes, + .topics = {0x04491edcd115127caedbd478e2e7895ed80c7847e903431f94f9cfa579cad47f_hash}}); + logs.push_back({ + .address = 0xe7fb22dfef11920312e4989a3a2b81e2ebf05986_bytes, + .topics = + { + 0x7f1fef85c4b037150d3675218e0cdb7cf38fea354759471e309f3354918a442f_hash, + 0xd85629c7eaae9ea4a10234fed31bc0aeda29b2683ebe0c1882499d272621f6b6_hash, + }, + .data = 0x2d690516512020171c1ec870f6ff45398cc8609250326be89915fb538e7b_bytes, + }); + auto const bloom = rpc::getLogsBloom(logs); + auto const bloomHex = toHex(bloom); + // clang-format off + auto constexpr expected = "00000000000000000081000000000000000000000000000000000002000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028000000000040000080000000400000000000000000000000000000000000000000000000000000000000000000000010000010000000000000000000000000000000001400000000000000008000000000000000000000000000000000"sv; + // clang-format on + BOOST_CHECK_EQUAL(bloomHex, expected); + } + // case hardhat + { + Logs logs{}; + logs.push_back({.address = 0x5fbdb2315678afecb367f032d93f642f64180aa3_bytes, + .topics = { + 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0_hash, + 0x0000000000000000000000000000000000000000000000000000000000000000_hash, + 0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266_hash, + }}); + // clang-format off + auto constexpr expected = "00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000040020000000000000100000800000000000000000000000000000000400000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000040000000200000000000000000000000002000000000000000000020000000000000000000000000000000000000000000000000000000000000000000"sv; + // clang-format on + auto const bloom = rpc::getLogsBloom(logs); + auto const bloomHex = toHex(bloom); + BOOST_CHECK_EQUAL(bloomHex, expected); + } +} +BOOST_AUTO_TEST_SUITE_END() +}; // namespace bcos::test \ No newline at end of file diff --git a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp index 9fa07c9402..4a8e9f44c4 100644 --- a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp +++ b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp @@ -59,27 +59,23 @@ class Web3TestFixture : public RPCFixture web3JsonRpc = rpc->web3JsonRpc(); BOOST_CHECK(web3JsonRpc != nullptr); } - Json::Value onRPCRequestWrapper(std::string_view request) + Json::Value onRPCRequestWrapper(std::string_view request, rpc::Sender _diySender = nullptr) { Json::Value value; - try + Json::Reader reader; + if (!_diySender) { std::promise promise; web3JsonRpc->onRPCRequest( request, [&promise](bcos::bytes resp) { promise.set_value(std::move(resp)); }); - Json::Reader reader; auto jsonBytes = promise.get_future().get(); std::string_view json( (char*)jsonBytes.data(), (char*)jsonBytes.data() + jsonBytes.size()); reader.parse(json.begin(), json.end(), value); } - catch (std::exception const& e) + else { - BCOS_LOG(ERROR) << (e).what(); - } - catch (...) - { - BCOS_LOG(ERROR) << "Unknown exception"; + web3JsonRpc->onRPCRequest(request, std::move(_diySender)); } return value; } @@ -283,13 +279,13 @@ BOOST_AUTO_TEST_CASE(handleLegacyTxTest) auto rawTxBytesRef = bcos::ref(rawTxBytes); Web3Transaction rawWeb3Tx; codec::rlp::decode(rawTxBytesRef, rawWeb3Tx); - auto response = onRPCRequestWrapper(request); - validRespCheck(response); - BOOST_CHECK(response["id"].asInt64() == 1132123); - BOOST_CHECK(response["result"].asString() == expectHash); + onRPCRequestWrapper(request, [](auto&&) {}); + // validRespCheck(response); + // BOOST_CHECK(response["id"].asInt64() == 1132123); + // BOOST_CHECK(response["result"].asString() == expectHash); std::vector hashes = {HashType(expectHash)}; - task::wait([&rawWeb3Tx]( - Web3TestFixture* self, decltype(hashes) m_hashes) -> task::Task { + task::wait([](Web3TestFixture* self, decltype(hashes) m_hashes, + decltype(rawWeb3Tx) rawWeb3Tx) -> task::Task { auto txs = co_await self->txPool->getTransactions(m_hashes); BOOST_CHECK(txs.size() == 1); BOOST_CHECK(txs[0]->hash() == m_hashes[0]); @@ -297,6 +293,8 @@ BOOST_AUTO_TEST_CASE(handleLegacyTxTest) BOOST_CHECK(!txs[0]->extraTransactionBytes().empty()); auto ref = bytesRef(const_cast(txs[0]->extraTransactionBytes().data()), txs[0]->extraTransactionBytes().size()); + auto const chainId = txs[0]->chainId(); + BOOST_CHECK(chainId == std::to_string(rawWeb3Tx.chainId.value_or(0))); Web3Transaction tx; bcos::codec::rlp::decode(ref, tx); BOOST_CHECK(tx.type == rawWeb3Tx.type); @@ -308,10 +306,7 @@ BOOST_AUTO_TEST_CASE(handleLegacyTxTest) BOOST_CHECK(tx.maxPriorityFeePerGas == rawWeb3Tx.maxPriorityFeePerGas); BOOST_CHECK(tx.gasLimit == rawWeb3Tx.gasLimit); BOOST_CHECK(tx.accessList == rawWeb3Tx.accessList); - // FIXME: chainId recover is wrong - // BOOST_CHECK(tx.chainId == rawWeb3Tx.chainId); - }(this, std::move(hashes))); - return rawWeb3Tx; + }(this, std::move(hashes), std::move(rawWeb3Tx))); }; // clang-format off @@ -339,10 +334,10 @@ BOOST_AUTO_TEST_CASE(handleEIP1559TxTest) auto rawTxBytesRef = bcos::ref(rawTxBytes); Web3Transaction rawWeb3Tx; codec::rlp::decode(rawTxBytesRef, rawWeb3Tx); - auto response = onRPCRequestWrapper(request); - validRespCheck(response); - BOOST_CHECK(response["id"].asInt64() == 1132123); - BOOST_CHECK(response["result"].asString() == expectHash); + onRPCRequestWrapper(request, [](auto&&) {}); + // validRespCheck(response); + // BOOST_CHECK(response["id"].asInt64() == 1132123); + // BOOST_CHECK(response["result"].asString() == expectHash); std::vector hashes = {HashType(expectHash)}; task::wait([&rawWeb3Tx]( Web3TestFixture* self, decltype(hashes) m_hashes) -> task::Task { @@ -394,10 +389,10 @@ BOOST_AUTO_TEST_CASE(handleEIP4844TxTest) auto rawTxBytesRef = bcos::ref(rawTxBytes); Web3Transaction rawWeb3Tx; codec::rlp::decode(rawTxBytesRef, rawWeb3Tx); - auto response = onRPCRequestWrapper(request); - validRespCheck(response); - BOOST_CHECK(response["id"].asInt64() == 1132123); - BOOST_CHECK(response["result"].asString() == expectHash); + onRPCRequestWrapper(request, [](auto&&) {}); + // validRespCheck(response); + // BOOST_CHECK(response["id"].asInt64() == 1132123); + // BOOST_CHECK(response["result"].asString() == expectHash); std::vector hashes = {HashType(expectHash)}; task::wait([&rawWeb3Tx]( Web3TestFixture* self, decltype(hashes) m_hashes) -> task::Task { diff --git a/bcos-scheduler/test/testChecksumAddress.cpp b/bcos-scheduler/test/testChecksumAddress.cpp index 3fe2cddcb1..636d0875ee 100644 --- a/bcos-scheduler/test/testChecksumAddress.cpp +++ b/bcos-scheduler/test/testChecksumAddress.cpp @@ -49,10 +49,17 @@ struct ChecksumFixture bcos::crypto::Hash::Ptr hashImpl; - void check(const std::string& addr) + void check(const std::string& addr, uint64_t chainId = 0) { auto ret = addr; - toCheckSumAddress(ret, hashImpl); + if (chainId != 0) + { + toCheckSumAddressWithChainId(ret, hashImpl, chainId); + } + else + { + toCheckSumAddress(ret, hashImpl); + } BOOST_CHECK_EQUAL(addr, ret); } }; @@ -69,5 +76,72 @@ BOOST_AUTO_TEST_CASE(checksumAddress) check("3C589CB0Be25f651b0563e052DEa63d3844C33e6"); } +BOOST_AUTO_TEST_CASE(checkEIP1191) +{ + // test case from https://eips.ethereum.org/EIPS/eip-1191 + auto const eth_mainnet = std::to_array({ + "27b1fdb04752bbc536007a920d24acb045561c26", + "3599689E6292b81B2d85451025146515070129Bb", + "42712D45473476b98452f434e72461577D686318", + "52908400098527886E0F7030069857D2E4169EE7", + "5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", + "6549f4939460DE12611948b3f82b88C3C8975323", + "66f9664f97F2b50F62D13eA064982f936dE76657", + "8617E340B3D01FA5F11F306F4090FD50E238070D", + "88021160C5C792225E4E5452585947470010289D", + "D1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", + "dbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", + "de709f2102306220921060314715629080e2fb77", + "fB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", + }); + + for (auto& addr : eth_mainnet) + { + check(addr, 1); + } + + auto const rsk_mainnet = std::to_array({ + "27b1FdB04752BBc536007A920D24ACB045561c26", + "3599689E6292B81B2D85451025146515070129Bb", + "42712D45473476B98452f434E72461577d686318", + "52908400098527886E0F7030069857D2E4169ee7", + "5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD", + "6549F4939460DE12611948B3F82B88C3C8975323", + "66F9664f97f2B50F62d13EA064982F936de76657", + "8617E340b3D01Fa5f11f306f4090fd50E238070D", + "88021160c5C792225E4E5452585947470010289d", + "D1220A0Cf47c7B9BE7a2e6ba89F429762E7B9adB", + "DBF03B407c01E7CD3cBea99509D93F8Dddc8C6FB", + "De709F2102306220921060314715629080e2FB77", + "Fb6916095cA1Df60bb79ce92cE3EA74c37c5d359", + }); + + for (auto& addr : rsk_mainnet) + { + check(addr, 30); + } + + auto const rsk_testnet = std::to_array({ + "27B1FdB04752BbC536007a920D24acB045561C26", + "3599689e6292b81b2D85451025146515070129Bb", + "42712D45473476B98452F434E72461577D686318", + "52908400098527886E0F7030069857D2e4169EE7", + "5aAeb6053F3e94c9b9A09F33669435E7EF1BEaEd", + "6549f4939460dE12611948b3f82b88C3c8975323", + "66f9664F97F2b50f62d13eA064982F936DE76657", + "8617e340b3D01fa5F11f306F4090Fd50e238070d", + "88021160c5C792225E4E5452585947470010289d", + "d1220a0CF47c7B9Be7A2E6Ba89f429762E7b9adB", + "dbF03B407C01E7cd3cbEa99509D93f8dDDc8C6fB", + "DE709F2102306220921060314715629080e2Fb77", + "Fb6916095CA1dF60bb79CE92ce3Ea74C37c5D359", + }); + + for (auto& addr : rsk_testnet) + { + check(addr, 31); + } +} + BOOST_AUTO_TEST_SUITE_END() } // namespace bcos::test \ No newline at end of file diff --git a/bcos-tars-protocol/bcos-tars-protocol/Common.h b/bcos-tars-protocol/bcos-tars-protocol/Common.h index 802e977c41..9466c0fa92 100644 --- a/bcos-tars-protocol/bcos-tars-protocol/Common.h +++ b/bcos-tars-protocol/bcos-tars-protocol/Common.h @@ -430,4 +430,20 @@ inline bcos::protocol::LogEntry toBcosLogEntry(bcostars::LogEntry const& _logEnt return bcos::protocol::LogEntry(bcos::bytes(_logEntry.address.begin(), _logEntry.address.end()), topics, bcos::bytes(_logEntry.data.begin(), _logEntry.data.end())); } + +inline bcos::protocol::LogEntry takeToBcosLogEntry(bcostars::LogEntry&& _logEntry) +{ + std::vector topics; + for (auto&& topicIt : _logEntry.topic) + { + bcos::h256 topic; + RANGES::move(topicIt.begin(), topicIt.end(), topic.mutableData().data()); + topics.push_back(std::move(topic)); + } + bcos::bytes address; + RANGES::move(std::move(_logEntry.address), std::back_inserter(address)); + bcos::bytes data; + RANGES::move(std::move(_logEntry.data), std::back_inserter(data)); + return {std::move(address), std::move(topics), std::move(data)}; +} } // namespace bcostars \ No newline at end of file diff --git a/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.cpp b/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.cpp index 463ff94006..35269eb3cc 100644 --- a/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.cpp +++ b/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.cpp @@ -102,14 +102,16 @@ bcos::protocol::LogEntries&& bcostars::protocol::TransactionReceiptImpl::takeLog { if (m_logEntries.empty()) { - m_logEntries.reserve(m_inner()->data.logEntries.size()); - for (auto& it : m_inner()->data.logEntries) + auto& innter = mutableInner(); + m_logEntries.reserve(innter.data.logEntries.size()); + for (auto& it : innter.data.logEntries) { - auto bcosLogEntry = toBcosLogEntry(it); - m_logEntries.emplace_back(std::move(bcosLogEntry)); + auto bcosLogEntry = takeToBcosLogEntry(std::move(it)); + m_logEntries.push_back(std::move(bcosLogEntry)); } + return std::move(m_logEntries); } - return std::move(m_logEntries); + return std::move(m_logEntries); } bcos::protocol::BlockNumber bcostars::protocol::TransactionReceiptImpl::blockNumber() const { diff --git a/bcos-utilities/bcos-utilities/FixedBytes.h b/bcos-utilities/bcos-utilities/FixedBytes.h index 25dcba9781..cd2538bc86 100644 --- a/bcos-utilities/bcos-utilities/FixedBytes.h +++ b/bcos-utilities/bcos-utilities/FixedBytes.h @@ -292,7 +292,7 @@ class FixedBytes byte* data() { return m_data.data(); } /// @returns a constant byte pointer to the object's data. byte const* data() const { return m_data.data(); } - + auto& mutableData() { return m_data; } /// @returns begin iterator. auto begin() const -> typename std::array::const_iterator { return m_data.begin(); } /// @returns end iterator. diff --git a/bcos-utilities/testutils/TestPromptFixture.h b/bcos-utilities/testutils/TestPromptFixture.h index 6f5c2f9da7..40e9978fc7 100644 --- a/bcos-utilities/testutils/TestPromptFixture.h +++ b/bcos-utilities/testutils/TestPromptFixture.h @@ -19,6 +19,7 @@ #pragma once #include "bcos-utilities/Common.h" +#include #include #include @@ -26,6 +27,14 @@ namespace bcos { namespace test { +inline bcos::bytes operator""_bytes(const char* s) noexcept +{ + return fromHexWithPrefix(std::string_view(s)); +} +inline bcos::crypto::HashType operator""_hash(const char* s) noexcept +{ + return bcos::crypto::HashType(std::string_view(s), bcos::crypto::HashType::FromHex); +} class TestPrompt { public: From bf0dbbd0faaee9127d7068c0c402b957f78ac82e Mon Sep 17 00:00:00 2001 From: jdkuang <1950618179@qq.com> Date: Sat, 11 May 2024 16:37:54 +0800 Subject: [PATCH 21/39] (rpc): adapt Ethereum event related interfaces in jsonrpc and web3jsonrpc (#4440) Co-authored-by: jdkuang --- bcos-rpc/bcos-rpc/RpcFactory.cpp | 15 +++- bcos-rpc/bcos-rpc/filter/FilterRequest.h | 2 +- bcos-rpc/bcos-rpc/filter/FilterSystem.cpp | 72 +++++++++++++++++ bcos-rpc/bcos-rpc/filter/FilterSystem.h | 19 ++--- .../bcos-rpc/jsonrpc/JsonRpcFilterSystem.h | 13 +++- bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp | 78 ++++++++++++++++++- bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.h | 19 ++++- .../bcos-rpc/jsonrpc/JsonRpcInterface.cpp | 17 ++++ bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h | 43 ++++++++++ .../bcos-rpc/web3jsonrpc/Web3FilterSystem.h | 25 ++++++ .../bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h | 5 +- .../web3jsonrpc/endpoints/Endpoints.h | 7 +- .../web3jsonrpc/endpoints/EthEndpoint.cpp | 58 +++++++------- .../web3jsonrpc/endpoints/EthEndpoint.h | 6 +- bcos-tool/bcos-tool/NodeConfig.cpp | 20 +++++ bcos-tool/bcos-tool/NodeConfig.h | 12 +++ lightnode/bcos-lightnode/rpc/LightNodeRPC.h | 45 +++++++++-- 17 files changed, 396 insertions(+), 60 deletions(-) create mode 100644 bcos-rpc/bcos-rpc/web3jsonrpc/Web3FilterSystem.h diff --git a/bcos-rpc/bcos-rpc/RpcFactory.cpp b/bcos-rpc/bcos-rpc/RpcFactory.cpp index 7d21749381..7fdbc98e39 100644 --- a/bcos-rpc/bcos-rpc/RpcFactory.cpp +++ b/bcos-rpc/bcos-rpc/RpcFactory.cpp @@ -31,7 +31,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -354,9 +356,11 @@ bcos::rpc::JsonRpcImpl_2_0::Ptr RpcFactory::buildJsonRpc(int sendTxTimeout, const std::shared_ptr& _wsService, GroupManager::Ptr _groupManager) { // JsonRpcImpl_2_0 - //* - auto jsonRpcInterface = - std::make_shared(_groupManager, m_gateway, _wsService); + auto filterSystem = std::make_shared(_groupManager, + m_nodeConfig->groupId(), m_nodeConfig->rpcFilterThreadSize(), + m_nodeConfig->rpcFilterTimeout(), m_nodeConfig->rpcMaxProcessBlock()); + auto jsonRpcInterface = std::make_shared( + _groupManager, m_gateway, _wsService, filterSystem); jsonRpcInterface->setSendTxTimeout(sendTxTimeout); auto httpServer = _wsService->httpServer(); if (httpServer) @@ -370,8 +374,11 @@ bcos::rpc::JsonRpcImpl_2_0::Ptr RpcFactory::buildJsonRpc(int sendTxTimeout, bcos::rpc::Web3JsonRpcImpl::Ptr RpcFactory::buildWeb3JsonRpc( int sendTxTimeout, boostssl::ws::WsService::Ptr _wsService, GroupManager::Ptr _groupManager) { + auto web3FilterSystem = std::make_shared(_groupManager, + m_nodeConfig->groupId(), m_nodeConfig->web3FilterThreadSize(), + m_nodeConfig->web3FilterTimeout(), m_nodeConfig->web3MaxProcessBlock()); auto web3JsonRpc = std::make_shared( - m_nodeConfig->groupId(), std::move(_groupManager), m_gateway, _wsService); + m_nodeConfig->groupId(), std::move(_groupManager), m_gateway, _wsService, web3FilterSystem); auto httpServer = _wsService->httpServer(); if (httpServer) { diff --git a/bcos-rpc/bcos-rpc/filter/FilterRequest.h b/bcos-rpc/bcos-rpc/filter/FilterRequest.h index c0da464259..5c2f7b6ab7 100644 --- a/bcos-rpc/bcos-rpc/filter/FilterRequest.h +++ b/bcos-rpc/bcos-rpc/filter/FilterRequest.h @@ -73,7 +73,7 @@ class FilterRequest m_topics[_index].insert(std::move(_topic)); } void setBlockHash(const std::string& _hash) { m_blockHash = _hash; } - void fromJson(const Json::Value& jParams, protocol::BlockNumber latest); + void fromJson(const Json::Value& jParams, protocol::BlockNumber latest = 0); bool checkBlockRange(); virtual int32_t InvalidParamsCode() = 0; diff --git a/bcos-rpc/bcos-rpc/filter/FilterSystem.cpp b/bcos-rpc/bcos-rpc/filter/FilterSystem.cpp index 8ad8097dbe..84687f8ea3 100644 --- a/bcos-rpc/bcos-rpc/filter/FilterSystem.cpp +++ b/bcos-rpc/bcos-rpc/filter/FilterSystem.cpp @@ -301,4 +301,76 @@ task::Task FilterSystem::getFilterLogsImpl(std::string_view groupId } auto params = m_factory->create(*(filter->params())); co_return co_await getLogsImpl(groupId, params, false); +} + +task::Task FilterSystem::getLogsImpl( + std::string_view groupId, FilterRequest::Ptr params, bool needCheckRange) +{ + // getLatestBlockNumber and getLogsInPool use the same ledger + auto ledger = getNodeService(groupId, "getLogsImpl")->ledger(); + if (!params->blockHash().empty()) + { // when blockHash is not empty, match logs within the specified block + auto matcher = m_matcher; + int64_t blockNumber = 0; + try + { + blockNumber = co_await ledger::getBlockNumber(*ledger, + bcos::crypto::HashType(params->blockHash(), bcos::crypto::HashType::FromHex)); + } + catch (std::exception& e) + { + BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParamsCode(), "unknown block")); + } + auto block = co_await ledger::getBlockData(*ledger, blockNumber, + bcos::ledger::HEADER | bcos::ledger::RECEIPTS | bcos::ledger::TRANSACTIONS_HASH); + Json::Value jArray(Json::arrayValue); + matcher->matches(params, block, jArray); + co_return jArray; + } + else + { + auto latestBlockNumber = getLatestBlockNumber(*ledger); + auto fromBlock = params->fromBlock(); + auto toBlock = params->toBlock(); + if (needCheckRange && !params->checkBlockRange()) + { + FILTER_LOG(WARNING) << LOG_BADGE("getLogsImpl") + << LOG_DESC("invalid block range params") + << LOG_KV("fromBlock", fromBlock) << LOG_KV("toBlock", toBlock); + BOOST_THROW_EXCEPTION( + JsonRpcException(InvalidParamsCode(), "invalid block range params")); + } + fromBlock = params->fromIsLatest() ? latestBlockNumber : fromBlock; + toBlock = params->toIsLatest() ? latestBlockNumber : toBlock; + if (fromBlock > latestBlockNumber) + { // the block of interest has not been generated yet + co_return Json::Value(Json::arrayValue); + } + params->setFromBlock(fromBlock); + params->setToBlock(std::min(toBlock, latestBlockNumber)); + // TODO:getLogsInPool + co_return co_await getLogsInternal(*ledger, std::move(params)); + } +} + +task::Task FilterSystem::getLogsInPool( + bcos::ledger::LedgerInterface::Ptr _ledger, FilterRequest::Ptr _params) +{ + co_return Json::Value(Json::arrayValue); +} + +task::Task FilterSystem::getLogsInternal( + bcos::ledger::LedgerInterface& ledger, FilterRequest::Ptr params) +{ + auto fromBlock = params->fromBlock(); + auto toBlock = params->toBlock(); + Json::Value jArray(Json::arrayValue); + auto matcher = m_matcher; + for (auto number = fromBlock; number <= toBlock; ++number) + { + auto block = co_await ledger::getBlockData(ledger, number, + bcos::ledger::HEADER | bcos::ledger::RECEIPTS | bcos::ledger::TRANSACTIONS_HASH); + matcher->matches(params, block, jArray); + } + co_return jArray; } \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/filter/FilterSystem.h b/bcos-rpc/bcos-rpc/filter/FilterSystem.h index 8f4ea71e84..237345c2ca 100644 --- a/bcos-rpc/bcos-rpc/filter/FilterSystem.h +++ b/bcos-rpc/bcos-rpc/filter/FilterSystem.h @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include #include @@ -124,9 +126,9 @@ class FilterSystem : public std::enable_shared_from_this } int64_t getLatestBlockNumber() { return getLatestBlockNumber(m_group); } + FilterRequestFactory::Ptr requestFactory() const { return m_factory; } ThreadPool::Ptr pool() const { return m_pool; } LogMatcher::Ptr matcher() const { return m_matcher; } - FilterRequest::Ptr createFilterRequest() { return m_factory->create(); } NodeService::Ptr getNodeService(std::string_view _groupID, std::string_view _command) const; protected: @@ -140,20 +142,11 @@ class FilterSystem : public std::enable_shared_from_this task::Task getLogChangeImpl(std::string_view groupId, Filter::Ptr filter); task::Task getFilterLogsImpl(std::string_view groupId, uint64_t filterID); task::Task getLogsImpl( - std::string_view groupId, FilterRequest::Ptr params, bool needCheckRange) - { - co_return Json::Value(Json::arrayValue); - } + std::string_view groupId, FilterRequest::Ptr params, bool needCheckRange); task::Task getLogsInPool( - bcos::ledger::LedgerInterface::Ptr ledger, FilterRequest::Ptr params) - { - co_return Json::Value(Json::arrayValue); - } + bcos::ledger::LedgerInterface::Ptr ledger, FilterRequest::Ptr params); task::Task getLogsInternal( - bcos::ledger::LedgerInterface& ledger, FilterRequest::Ptr params) - { - co_return Json::Value(Json::arrayValue); - } + bcos::ledger::LedgerInterface& ledger, FilterRequest::Ptr params); protected: virtual int32_t InvalidParamsCode() = 0; diff --git a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcFilterSystem.h b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcFilterSystem.h index f2592731af..113b3c5124 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcFilterSystem.h +++ b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcFilterSystem.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include namespace bcos @@ -29,7 +30,17 @@ class JsonRpcFilterRequestFactory : public FilterRequestFactory } }; -// class JsonRpcFilterSystem; +class JsonRpcFilterSystem : public FilterSystem +{ +public: + JsonRpcFilterSystem(GroupManager::Ptr groupManager, const std::string& groupId, int threadNum, + int filterTimeout, int maxBlockProcessPerReq) + : FilterSystem(groupManager, groupId, std::make_shared(), + threadNum, filterTimeout, maxBlockProcessPerReq) + {} + + virtual int32_t InvalidParamsCode() override { return JsonRpcError::InvalidParams; } +}; } // namespace rpc } // namespace bcos \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp index 32e1bf2983..6bf392bd19 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp +++ b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp @@ -59,10 +59,11 @@ using namespace boost::archive::iterators; JsonRpcImpl_2_0::JsonRpcImpl_2_0(GroupManager::Ptr _groupManager, bcos::gateway::GatewayInterface::Ptr _gatewayInterface, - std::shared_ptr _wsService) + std::shared_ptr _wsService, FilterSystem::Ptr filterSystem) : m_groupManager(std::move(_groupManager)), m_gatewayInterface(std::move(_gatewayInterface)), - m_wsService(std::move(_wsService)) + m_wsService(std::move(_wsService)), + m_filterSystem(filterSystem) { m_wsService->registerMsgHandler(bcos::protocol::MessageType::RPC_REQUEST, boost::bind(&JsonRpcImpl_2_0::handleRpcRequest, this, boost::placeholders::_1, @@ -1431,6 +1432,79 @@ void JsonRpcImpl_2_0::getGroupPeers(std::string_view _groupID, RespFunc _respFun }); } +void JsonRpcImpl_2_0::newBlockFilter(std::string_view _groupID, RespFunc _respFunc) +{ + task::wait( + [](JsonRpcImpl_2_0* self, std::string_view groupID, RespFunc respFunc) -> task::Task { + Json::Value jRes = co_await self->filterSystem().newBlockFilter(groupID); + respFunc(nullptr, jRes); + }(this, _groupID, std::move(_respFunc))); +} + +void JsonRpcImpl_2_0::newPendingTransactionFilter(std::string_view _groupID, RespFunc _respFunc) +{ + task::wait( + [](JsonRpcImpl_2_0* self, std::string_view groupID, RespFunc respFunc) -> task::Task { + Json::Value jRes = co_await self->filterSystem().newPendingTxFilter(groupID); + respFunc(nullptr, jRes); + }(this, _groupID, std::move(_respFunc))); +} + +void JsonRpcImpl_2_0::newFilter( + std::string_view _groupID, const Json::Value& jParams, RespFunc _respFunc) +{ + task::wait([&jParams](JsonRpcImpl_2_0* self, std::string_view groupID, + RespFunc respFunc) -> task::Task { + Json::Value jRes; + auto params = self->filterSystem().requestFactory()->create(); + params->fromJson(jParams); + jRes = co_await self->filterSystem().newFilter(groupID, std::move(params)); + respFunc(nullptr, jRes); + }(this, _groupID, std::move(_respFunc))); +} + +void JsonRpcImpl_2_0::uninstallFilter( + std::string_view _groupID, std::string_view filterID, RespFunc _respFunc) +{ + task::wait([](JsonRpcImpl_2_0* self, std::string_view groupID, uint64_t id, + RespFunc respFunc) -> task::Task { + Json::Value jRes = co_await self->filterSystem().uninstallFilter(groupID, id); + respFunc(nullptr, jRes); + }(this, _groupID, fromQuantity(std::string(filterID)), std::move(_respFunc))); +} + +void JsonRpcImpl_2_0::getFilterChanges( + std::string_view _groupID, std::string_view filterID, RespFunc _respFunc) +{ + task::wait([](JsonRpcImpl_2_0* self, std::string_view groupID, uint64_t id, + RespFunc respFunc) -> task::Task { + Json::Value jRes = co_await self->filterSystem().getFilterChanges(groupID, id); + respFunc(nullptr, jRes); + }(this, _groupID, fromQuantity(std::string(filterID)), std::move(_respFunc))); +} + +void JsonRpcImpl_2_0::getFilterLogs( + std::string_view _groupID, std::string_view filterID, RespFunc _respFunc) +{ + task::wait([](JsonRpcImpl_2_0* self, std::string_view groupID, uint64_t id, + RespFunc respFunc) -> task::Task { + Json::Value jRes = co_await self->filterSystem().getFilterLogs(groupID, id); + respFunc(nullptr, jRes); + }(this, _groupID, fromQuantity(std::string(filterID)), std::move(_respFunc))); +} + +void JsonRpcImpl_2_0::getLogs( + std::string_view _groupID, const Json::Value& jParams, RespFunc _respFunc) +{ + task::wait([](JsonRpcImpl_2_0* self, std::string_view groupID, const Json::Value& jParams, + RespFunc respFunc) -> task::Task { + auto params = self->filterSystem().requestFactory()->create(); + params->fromJson(jParams); + Json::Value jRes = co_await self->filterSystem().getLogs(groupID, std::move(params)); + respFunc(nullptr, jRes); + }(this, _groupID, jParams, std::move(_respFunc))); +} + void JsonRpcImpl_2_0::execCall( NodeService::Ptr nodeService, protocol::Transaction::Ptr _tx, bcos::rpc::RespFunc _respFunc) { diff --git a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.h b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.h index 57e80cf145..c708e29e17 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.h +++ b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.h @@ -25,6 +25,7 @@ #include "bcos-rpc/validator/CallValidator.h" #include #include +#include #include #include #include @@ -40,7 +41,7 @@ class JsonRpcImpl_2_0 : public JsonRpcInterface, using Ptr = std::shared_ptr; JsonRpcImpl_2_0(GroupManager::Ptr _groupManager, bcos::gateway::GatewayInterface::Ptr _gatewayInterface, - std::shared_ptr _wsService); + std::shared_ptr _wsService, FilterSystem::Ptr filterSystem); ~JsonRpcImpl_2_0() override = default; void setClientID(std::string_view _clientID) { m_clientID = _clientID; } @@ -118,6 +119,19 @@ class JsonRpcImpl_2_0 : public JsonRpcInterface, void getGroupNodeInfo( std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) override; + // filter interface + void newBlockFilter(std::string_view _groupID, RespFunc _respFunc) override; + void newPendingTransactionFilter(std::string_view _groupID, RespFunc _respFunc) override; + void newFilter( + std::string_view _groupID, const Json::Value& params, RespFunc _respFunc) override; + void uninstallFilter( + std::string_view _groupID, std::string_view filterID, RespFunc _respFunc) override; + void getFilterChanges( + std::string_view _groupID, std::string_view filterID, RespFunc _respFunc) override; + void getFilterLogs( + std::string_view _groupID, std::string_view filterID, RespFunc _respFunc) override; + void getLogs(std::string_view _groupID, const Json::Value& params, RespFunc _respFunc) override; + void getGroupBlockNumber(RespFunc _respFunc) override; void setNodeInfo(const NodeInfo& _nodeInfo) { m_nodeInfo = _nodeInfo; } @@ -153,6 +167,8 @@ class JsonRpcImpl_2_0 : public JsonRpcInterface, } } + FilterSystem& filterSystem() { return *m_filterSystem; } + protected: void gatewayInfoToJson(Json::Value& _response, bcos::gateway::GatewayInfo::Ptr _gatewayInfo); void gatewayInfoToJson(Json::Value& _response, bcos::gateway::GatewayInfo::Ptr _localP2pInfo, @@ -170,6 +186,7 @@ class JsonRpcImpl_2_0 : public JsonRpcInterface, GroupManager::Ptr m_groupManager; bcos::gateway::GatewayInterface::Ptr m_gatewayInterface; std::shared_ptr m_wsService; + FilterSystem::Ptr m_filterSystem; NodeInfo m_nodeInfo; // Note: here clientID must non-empty for the rpc will set clientID as source for the tx for diff --git a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.cpp b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.cpp index fdce34b859..566e4d3a63 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.cpp +++ b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.cpp @@ -66,6 +66,23 @@ void JsonRpcInterface::initMethod() m_methodToFunc["getGroupNodeInfo"] = std::bind( &JsonRpcInterface::getGroupNodeInfoI, this, std::placeholders::_1, std::placeholders::_2); + // filter interface + m_methodToFunc["newBlockFilter"] = std::bind( + &JsonRpcInterface::newBlockFilterI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["newPendingTransactionFilter"] = + std::bind(&JsonRpcInterface::newPendingTransactionFilterI, this, std::placeholders::_1, + std::placeholders::_2); + m_methodToFunc["newFilter"] = std::bind( + &JsonRpcInterface::newFilterI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["uninstallFilter"] = std::bind( + &JsonRpcInterface::uninstallFilterI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getFilterChanges"] = std::bind( + &JsonRpcInterface::getFilterChangesI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getFilterLogs"] = std::bind( + &JsonRpcInterface::getFilterLogsI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getLogs"] = + std::bind(&JsonRpcInterface::getLogsI, this, std::placeholders::_1, std::placeholders::_2); + for (const auto& method : m_methodToFunc) { RPC_IMPL_LOG(INFO) << LOG_BADGE("initMethod") << LOG_KV("method", method.first); diff --git a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h index 7095cc3f78..30e7eaafe3 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h +++ b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h @@ -121,6 +121,20 @@ class JsonRpcInterface virtual void getGroupBlockNumber(RespFunc _respFunc) = 0; + // filter interface + virtual void newBlockFilter(std::string_view _groupID, RespFunc _respFunc) = 0; + virtual void newPendingTransactionFilter(std::string_view _groupID, RespFunc _respFunc) = 0; + virtual void newFilter( + std::string_view _groupID, const Json::Value& params, RespFunc _respFunc) = 0; + virtual void uninstallFilter( + std::string_view _groupID, std::string_view filterID, RespFunc _respFunc) = 0; + virtual void getFilterChanges( + std::string_view _groupID, std::string_view filterID, RespFunc _respFunc) = 0; + virtual void getFilterLogs( + std::string_view _groupID, std::string_view filterID, RespFunc _respFunc) = 0; + virtual void getLogs( + std::string_view _groupID, const Json::Value& params, RespFunc _respFunc) = 0; + void onRPCRequest(std::string_view _requestBody, Sender _sender); protected: @@ -288,6 +302,35 @@ class JsonRpcInterface { getGroupNodeInfo(toView(_req[0u]), toView(_req[1u]), std::move(_respFunc)); } + // filter interface + void newBlockFilterI(const Json::Value& _req, RespFunc _respFunc) + { + newBlockFilter(toView(_req[0u]), std::move(_respFunc)); + } + void newPendingTransactionFilterI(const Json::Value& _req, RespFunc _respFunc) + { + newPendingTransactionFilter(toView(_req[0u]), std::move(_respFunc)); + } + void newFilterI(const Json::Value& _req, RespFunc _respFunc) + { + newFilter(toView(_req[0u]), _req[1u], std::move(_respFunc)); + } + void uninstallFilterI(const Json::Value& _req, RespFunc _respFunc) + { + uninstallFilter(toView(_req[0u]), toView(_req[1u]), std::move(_respFunc)); + } + void getFilterChangesI(const Json::Value& _req, RespFunc _respFunc) + { + getFilterChanges(toView(_req[0u]), toView(_req[1u]), std::move(_respFunc)); + } + void getFilterLogsI(const Json::Value& _req, RespFunc _respFunc) + { + getFilterLogs(toView(_req[0u]), toView(_req[1u]), std::move(_respFunc)); + } + void getLogsI(const Json::Value& _req, RespFunc _respFunc) + { + getLogs(toView(_req[0u]), _req[1u], std::move(_respFunc)); + } }; void parseRpcRequestJson(std::string_view _requestBody, JsonRequest& _jsonRequest); bcos::bytes toStringResponse(JsonResponse _jsonResponse); diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3FilterSystem.h b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3FilterSystem.h new file mode 100644 index 0000000000..f48bff1b07 --- /dev/null +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3FilterSystem.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + +namespace bcos +{ +namespace rpc +{ + +class Web3FilterSystem : public FilterSystem +{ +public: + Web3FilterSystem(GroupManager::Ptr groupManager, const std::string& groupId, int threadNum, + int filterTimeout, int maxBlockProcessPerReq) + : FilterSystem(groupManager, groupId, std::make_shared(), threadNum, + filterTimeout, maxBlockProcessPerReq) + {} + + virtual int32_t InvalidParamsCode() override { return Web3JsonRpcError::Web3DefautError; } +}; + +} // namespace rpc +} // namespace bcos \ No newline at end of file diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h index 7532e6887a..3a19a91eb3 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3JsonRpcImpl.h @@ -40,12 +40,13 @@ class Web3JsonRpcImpl : public std::enable_shared_from_this using Sender = std::function; Web3JsonRpcImpl(std::string _groupId, bcos::rpc::GroupManager::Ptr _groupManager, bcos::gateway::GatewayInterface::Ptr _gatewayInterface, - std::shared_ptr _wsService) + std::shared_ptr _wsService, + FilterSystem::Ptr filterSystem) : m_groupManager(std::move(_groupManager)), m_gatewayInterface(std::move(_gatewayInterface)), m_wsService(std::move(_wsService)), m_groupId(std::move(_groupId)), - m_endpoints(m_groupManager->getNodeService(m_groupId, "")) + m_endpoints(m_groupManager->getNodeService(m_groupId, ""), filterSystem) {} ~Web3JsonRpcImpl() = default; diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Endpoints.h b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Endpoints.h index 8e9fc14f57..a2fa630d4c 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Endpoints.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/Endpoints.h @@ -30,9 +30,12 @@ namespace bcos::rpc class Endpoints : protected EthEndpoint, NetEndpoint, Web3Endpoint { public: - explicit Endpoints(NodeService::Ptr _nodeService) - : EthEndpoint(_nodeService), NetEndpoint(_nodeService), Web3Endpoint(_nodeService) + explicit Endpoints(NodeService::Ptr _nodeService, FilterSystem::Ptr filterSystem) + : EthEndpoint(_nodeService, filterSystem), + NetEndpoint(_nodeService), + Web3Endpoint(_nodeService) {} + ~Endpoints() override = default; Endpoints(const Endpoints&) = delete; Endpoints& operator=(const Endpoints&) = delete; diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp index 7249453b7d..cc63d7eb81 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp @@ -691,65 +691,67 @@ task::Task EthEndpoint::getUncleByBlockNumberAndIndex( buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::newFilter(const Json::Value&, Json::Value&) +task::Task EthEndpoint::newFilter(const Json::Value& request, Json::Value& response) { // params: filter(FILTER) // result: filterId(QTY) - BOOST_THROW_EXCEPTION( - JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); - + Json::Value jParams = request[0U]; + auto params = m_filterSystem->requestFactory()->create(); + params->fromJson(jParams); + Json::Value result = co_await m_filterSystem->newFilter(params); + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::newBlockFilter(const Json::Value&, Json::Value&) +task::Task EthEndpoint::newBlockFilter(const Json::Value&, Json::Value& response) { // result: filterId(QTY) - BOOST_THROW_EXCEPTION( - JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); - + Json::Value result = co_await m_filterSystem->newBlockFilter(); + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::newPendingTransactionFilter(const Json::Value&, Json::Value&) +task::Task EthEndpoint::newPendingTransactionFilter(const Json::Value&, Json::Value& response) { // result: filterId(QTY) - BOOST_THROW_EXCEPTION( - JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); - + Json::Value result = co_await m_filterSystem->newPendingTxFilter(); + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::uninstallFilter(const Json::Value&, Json::Value&) +task::Task EthEndpoint::uninstallFilter(const Json::Value& request, Json::Value& response) { // params: filterId(QTY) // result: success(Boolean) - BOOST_THROW_EXCEPTION( - JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); - + auto const id = fromQuantity(std::string(toView(request[0U]))); + Json::Value result = co_await m_filterSystem->uninstallFilter(id); + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::getFilterChanges(const Json::Value&, Json::Value&) +task::Task EthEndpoint::getFilterChanges(const Json::Value& request, Json::Value& response) { // params: filterId(QTY) // result: logs(ARRAY) - BOOST_THROW_EXCEPTION( - JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); - + auto const id = fromQuantity(std::string(toView(request[0U]))); + Json::Value result = co_await m_filterSystem->getFilterChanges(id); + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::getFilterLogs(const Json::Value&, Json::Value&) +task::Task EthEndpoint::getFilterLogs(const Json::Value& request, Json::Value& response) { // params: filterId(QTY) // result: logs(ARRAY) - BOOST_THROW_EXCEPTION( - JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); - + auto const id = fromQuantity(std::string(toView(request[0U]))); + Json::Value result = co_await m_filterSystem->getFilterLogs(id); + buildJsonContent(result, response); co_return; } -task::Task EthEndpoint::getLogs(const Json::Value&, Json::Value&) +task::Task EthEndpoint::getLogs(const Json::Value& request, Json::Value& response) { // params: filter(FILTER) // result: logs(ARRAY) - BOOST_THROW_EXCEPTION( - JsonRpcException(MethodNotFound, "This API has not been implemented yet!")); - + Json::Value jParams = request[0U]; + auto params = m_filterSystem->requestFactory()->create(); + params->fromJson(jParams); + Json::Value result = co_await m_filterSystem->getLogs(params); + buildJsonContent(result, response); co_return; } task::Task> EthEndpoint::getBlockNumberByTag( diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h index 2c7306040d..95ea0b1e00 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -37,7 +38,9 @@ namespace bcos::rpc class EthEndpoint { public: - explicit EthEndpoint(NodeService::Ptr nodeService) : m_nodeService(std::move(nodeService)) {} + EthEndpoint(NodeService::Ptr nodeService, FilterSystem::Ptr filterSystem) + : m_nodeService(std::move(nodeService)), m_filterSystem(std::move(filterSystem)) + {} virtual ~EthEndpoint() = default; protected: @@ -84,6 +87,7 @@ class EthEndpoint private: NodeService::Ptr m_nodeService; + FilterSystem::Ptr m_filterSystem; }; } // namespace bcos::rpc diff --git a/bcos-tool/bcos-tool/NodeConfig.cpp b/bcos-tool/bcos-tool/NodeConfig.cpp index 3de399ac43..fe1f34f5d4 100644 --- a/bcos-tool/bcos-tool/NodeConfig.cpp +++ b/bcos-tool/bcos-tool/NodeConfig.cpp @@ -360,10 +360,17 @@ void NodeConfig::loadRpcConfig(boost::property_tree::ptree const& _pt) thread_count=16 sm_ssl=false disable_ssl=false + filter_thread_count=4 + ; 300s + filter_timeout=300 + filter_max_process_block=10 */ std::string listenIP = _pt.get("rpc.listen_ip", "0.0.0.0"); int listenPort = _pt.get("rpc.listen_port", 20200); int threadCount = _pt.get("rpc.thread_count", 8); + int filterThreadCount = _pt.get("rpc.filter_thread_count", 4); + int filterTimeout = _pt.get("rpc.filter_timeout", 300); + int maxProcessBlock = _pt.get("rpc.filter_max_process_block", 10); bool smSsl = _pt.get("rpc.sm_ssl", false); bool disableSsl = _pt.get("rpc.disable_ssl", false); bool needRetInput = _pt.get("rpc.return_input_params", true); @@ -373,6 +380,9 @@ void NodeConfig::loadRpcConfig(boost::property_tree::ptree const& _pt) m_rpcThreadPoolSize = threadCount; m_rpcDisableSsl = disableSsl; m_rpcSmSsl = smSsl; + m_rpcFilterThreadSize = filterThreadCount; + m_rpcFilterTimeout = filterTimeout; + m_rpcMaxProcessBlock = maxProcessBlock; g_BCOSConfig.setNeedRetInput(needRetInput); NodeConfig_LOG(INFO) << LOG_DESC("loadRpcConfig") << LOG_KV("listenIP", listenIP) @@ -389,16 +399,26 @@ void NodeConfig::loadWeb3RpcConfig(boost::property_tree::ptree const& _pt) listen_ip=127.0.0.1 listen_port=8545 thread_count=16 + filter_thread_count=4 + ; 300s + filter_timeout=300 + filter_max_process_block=10 */ const std::string listenIP = _pt.get("web3_rpc.listen_ip", "127.0.0.1"); const int listenPort = _pt.get("web3_rpc.listen_port", 8545); const int threadCount = _pt.get("web3_rpc.thread_count", 8); + const int filterThreadCount = _pt.get("web3_rpc.filter_thread_count", 4); + const int filterTimeout = _pt.get("web3_rpc.filter_timeout", 300); + const int maxProcessBlock = _pt.get("web3_rpc.filter_max_process_block", 10); const bool enableWeb3Rpc = _pt.get("web3_rpc.enable", false); m_web3RpcListenIP = listenIP; m_web3RpcListenPort = listenPort; m_web3RpcThreadSize = threadCount; m_enableWeb3Rpc = enableWeb3Rpc; + m_web3FilterThreadSize = filterThreadCount; + m_web3FilterTimeout = filterTimeout; + m_web3MaxProcessBlock = maxProcessBlock; NodeConfig_LOG(INFO) << LOG_DESC("loadWeb3RpcConfig") << LOG_KV("enableWeb3Rpc", enableWeb3Rpc) << LOG_KV("listenIP", listenIP) << LOG_KV("listenPort", listenPort) diff --git a/bcos-tool/bcos-tool/NodeConfig.h b/bcos-tool/bcos-tool/NodeConfig.h index 4d9c4208c5..2af8a520ad 100644 --- a/bcos-tool/bcos-tool/NodeConfig.h +++ b/bcos-tool/bcos-tool/NodeConfig.h @@ -178,14 +178,20 @@ class NodeConfig const std::string& rpcListenIP() const { return m_rpcListenIP; } uint16_t rpcListenPort() const { return m_rpcListenPort; } uint32_t rpcThreadPoolSize() const { return m_rpcThreadPoolSize; } + uint32_t rpcFilterTimeout() const { return m_rpcFilterTimeout; } + uint32_t rpcMaxProcessBlock() const { return m_rpcMaxProcessBlock; } bool rpcSmSsl() const { return m_rpcSmSsl; } bool rpcDisableSsl() const { return m_rpcDisableSsl; } + uint32_t rpcFilterThreadSize() const { return m_rpcFilterThreadSize; } // the web3 rpc configurations bool enableWeb3Rpc() const { return m_enableWeb3Rpc; } const std::string& web3RpcListenIP() const { return m_web3RpcListenIP; } uint16_t web3RpcListenPort() const { return m_web3RpcListenPort; } uint32_t web3RpcThreadSize() const { return m_web3RpcThreadSize; } + uint32_t web3FilterThreadSize() const { return m_web3FilterThreadSize; } + uint32_t web3FilterTimeout() const { return m_web3FilterTimeout; } + uint32_t web3MaxProcessBlock() const { return m_web3MaxProcessBlock; } // the gateway configurations const std::string& p2pListenIP() const { return m_p2pListenIP; } @@ -425,6 +431,9 @@ class NodeConfig std::string m_rpcListenIP; uint16_t m_rpcListenPort; uint32_t m_rpcThreadPoolSize; + uint32_t m_rpcFilterThreadSize; + uint32_t m_rpcFilterTimeout; + uint32_t m_rpcMaxProcessBlock; bool m_rpcSmSsl; bool m_rpcDisableSsl = false; @@ -433,6 +442,9 @@ class NodeConfig std::string m_web3RpcListenIP; uint16_t m_web3RpcListenPort; uint32_t m_web3RpcThreadSize; + uint32_t m_web3FilterThreadSize; + uint32_t m_web3FilterTimeout; + uint32_t m_web3MaxProcessBlock; // config for gateway std::string m_p2pListenIP; diff --git a/lightnode/bcos-lightnode/rpc/LightNodeRPC.h b/lightnode/bcos-lightnode/rpc/LightNodeRPC.h index f02b1ed772..71853c16b5 100644 --- a/lightnode/bcos-lightnode/rpc/LightNodeRPC.h +++ b/lightnode/bcos-lightnode/rpc/LightNodeRPC.h @@ -314,10 +314,11 @@ class LightNodeRPC : public bcos::rpc::JsonRpcInterface { // Check transaction merkle crypto::merkle::Merkle merkle(self->m_hasher.clone()); - auto hashesRange = block.transactionsMetaData | RANGES::views::transform([ - ](const bcostars::TransactionMetaData& transactionMetaData) -> auto& { - return transactionMetaData.hash; - }); + auto hashesRange = + block.transactionsMetaData | + RANGES::views::transform( + [](const bcostars::TransactionMetaData& transactionMetaData) + -> auto& { return transactionMetaData.hash; }); std::vector merkles; merkle.generateMerkle(hashesRange, merkles); @@ -574,7 +575,41 @@ class LightNodeRPC : public bcos::rpc::JsonRpcInterface Json::Value value; _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); } - + void newBlockFilter(std::string_view, RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + void newPendingTransactionFilter(std::string_view, RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + void newFilter(std::string_view, const Json::Value&, RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + void uninstallFilter(std::string_view, std::string_view, RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + void getFilterChanges(std::string_view, std::string_view, RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + void getFilterLogs(std::string_view, std::string_view, RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + void getLogs(std::string_view, const Json::Value&, RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } void getGroupBlockNumber(RespFunc respFunc) override { LIGHTNODE_LOG(INFO) << "RPC get group block number request"; From cc05d6a73067ad28bfe36194a2209bc672408c50 Mon Sep 17 00:00:00 2001 From: kyonRay Date: Tue, 14 May 2024 15:09:56 +0800 Subject: [PATCH 22/39] (rpc,ledger): fix compile error when merge 3.8.0. --- bcos-framework/bcos-framework/ledger/LedgerInterface.h | 2 ++ bcos-ledger/src/libledger/Ledger.h | 4 ++-- bcos-ledger/src/libledger/LedgerMethods.cpp | 3 +++ bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp | 2 ++ bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h | 9 +++++---- .../bcos-rpc/web3jsonrpc/model/TransactionResponse.h | 4 ++-- bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h | 1 + 7 files changed, 17 insertions(+), 8 deletions(-) diff --git a/bcos-framework/bcos-framework/ledger/LedgerInterface.h b/bcos-framework/bcos-framework/ledger/LedgerInterface.h index 1d917ef297..da86497aab 100644 --- a/bcos-framework/bcos-framework/ledger/LedgerInterface.h +++ b/bcos-framework/bcos-framework/ledger/LedgerInterface.h @@ -28,6 +28,8 @@ #include "../storage/StorageInterface.h" #include "LedgerTypeDef.h" #include +#include +#include #include #include #include diff --git a/bcos-ledger/src/libledger/Ledger.h b/bcos-ledger/src/libledger/Ledger.h index 26035e291e..9993f197ec 100644 --- a/bcos-ledger/src/libledger/Ledger.h +++ b/bcos-ledger/src/libledger/Ledger.h @@ -137,10 +137,10 @@ class Ledger : public LedgerInterface auto [compatibilityVersionStr, _] = entry->template getObject(); auto const version = bcos::tool::toVersionNumber(compatibilityVersionStr); auto stateStorage = stateStorageFactory.createStateStorage( - m_storage, version, true, std::move(keyPageIgnoreTables)); + m_storage, version, true, false, std::move(keyPageIgnoreTables)); return stateStorage; } - return std::make_shared(m_storage); + return std::make_shared(m_storage, true); } private: diff --git a/bcos-ledger/src/libledger/LedgerMethods.cpp b/bcos-ledger/src/libledger/LedgerMethods.cpp index 0fd96c67ef..5a932c9728 100644 --- a/bcos-ledger/src/libledger/LedgerMethods.cpp +++ b/bcos-ledger/src/libledger/LedgerMethods.cpp @@ -1,6 +1,9 @@ #include "LedgerMethods.h" #include "bcos-tool/VersionConverter.h" #include "utilities/Common.h" + +#include + #include #include diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp index cc63d7eb81..935fb3ee1e 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ #include #include #include +#include using namespace bcos; using namespace bcos::rpc; diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h index e7904a166f..f558265db2 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/ReceiptResponse.h @@ -64,7 +64,7 @@ namespace bcos::rpc result["blockHash"] = blockHash.hexPrefixed(); result["blockNumber"] = toQuantity(blockNumber); auto from = toHex(tx->sender()); - toChecksumAddress(from, bcos::crypto::keccak256Hash(from).hex()); + toChecksumAddress(from, bcos::crypto::keccak256Hash(bcos::bytesConstRef(from)).hex()); result["from"] = "0x" + std::move(from); if (tx->to().empty()) { @@ -74,7 +74,7 @@ namespace bcos::rpc { auto toView = tx->to(); auto to = std::string(toView.starts_with("0x") ? toView.substr(2) : toView); - toChecksumAddress(to, bcos::crypto::keccak256Hash(to).hex()); + toChecksumAddress(to, bcos::crypto::keccak256Hash(bcos::bytesConstRef(to)).hex()); result["to"] = "0x" + std::move(to); } result["cumulativeGasUsed"] = "0x0"; @@ -88,7 +88,8 @@ namespace bcos::rpc else { auto contractAddress = std::string(receipt->contractAddress()); - toChecksumAddress(contractAddress, bcos::crypto::keccak256Hash(contractAddress).hex()); + toChecksumAddress(contractAddress, + bcos::crypto::keccak256Hash(bcos::bytesConstRef(contractAddress)).hex()); result["contractAddress"] = "0x" + std::move(contractAddress); } result["logs"] = Json::arrayValue; @@ -98,7 +99,7 @@ namespace bcos::rpc { Json::Value log; auto address = std::string(receiptLog[i].address()); - toChecksumAddress(address, bcos::crypto::keccak256Hash(address).hex()); + toChecksumAddress(address, bcos::crypto::keccak256Hash(bcos::bytesConstRef(address)).hex()); log["address"] = "0x" + std::move(address); log["topics"] = Json::arrayValue; for (const auto& topic : receiptLog[i].topics()) diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h index 26e125fb86..0266ff16fe 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h @@ -59,7 +59,7 @@ static void combineTxResponse(Json::Value& result, bcos::protocol::Transaction:: result["blockNumber"] = toQuantity(blockNumber); result["transactionIndex"] = toQuantity(transactionIndex); auto from = toHex(tx->sender()); - toChecksumAddress(from, bcos::crypto::keccak256Hash(from).hex()); + toChecksumAddress(from, bcos::crypto::keccak256Hash(bcos::bytesConstRef(from)).hex()); result["from"] = "0x" + std::move(from); if (tx->to().empty()) { @@ -69,7 +69,7 @@ static void combineTxResponse(Json::Value& result, bcos::protocol::Transaction:: { auto toView = tx->to(); auto to = std::string(toView.starts_with("0x") ? toView.substr(2) : toView); - toChecksumAddress(to, bcos::crypto::keccak256Hash(to).hex()); + toChecksumAddress(to, bcos::crypto::keccak256Hash(bcos::bytesConstRef(to)).hex()); result["to"] = "0x" + std::move(to); } result["gas"] = toQuantity(tx->gasLimit()); diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h index c9c2054895..9cc6a8ad3a 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h @@ -27,6 +27,7 @@ #include #include #include +#include #include namespace bcos { From eb3868edc3a968b2963e6ed6f1f056c0398b2c8a Mon Sep 17 00:00:00 2001 From: wenlinli <1574249665@qq.com> Date: Wed, 8 May 2024 14:05:15 +0800 Subject: [PATCH 23/39] revert log change to avoid compatibility problems for 3.7.2 (#4436) --- bcos-executor/src/precompiled/ShardingPrecompiled.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bcos-executor/src/precompiled/ShardingPrecompiled.cpp b/bcos-executor/src/precompiled/ShardingPrecompiled.cpp index d24978bd23..6d5c5db4c2 100644 --- a/bcos-executor/src/precompiled/ShardingPrecompiled.cpp +++ b/bcos-executor/src/precompiled/ShardingPrecompiled.cpp @@ -209,7 +209,7 @@ void ShardingPrecompiled::makeShard( { int32_t code; codec.decode(ref(_callParameters->execResult()), code); - message = "code: " + std::to_string(code); + message = "errorCode: " + std::to_string(code); } PRECOMPILED_LOG(WARNING) << LOG_BADGE("ShardPrecompiled") << LOG_DESC("BFS makeDir error: ") From 95b6422ebda31ec0ac773cbc3924ff0a89e812cf Mon Sep 17 00:00:00 2001 From: jdkuang <1950618179@qq.com> Date: Thu, 16 May 2024 15:37:08 +0800 Subject: [PATCH 24/39] (rpc): remove the threadpool in FilterSystem (#4451) Co-authored-by: jdkuang --- bcos-rpc/bcos-rpc/RpcFactory.cpp | 12 ++++++------ bcos-rpc/bcos-rpc/filter/FilterSystem.cpp | 10 +--------- bcos-rpc/bcos-rpc/filter/FilterSystem.h | 9 +-------- bcos-rpc/bcos-rpc/jsonrpc/JsonRpcFilterSystem.h | 4 ++-- bcos-rpc/bcos-rpc/web3jsonrpc/Web3FilterSystem.h | 6 +++--- bcos-tool/bcos-tool/NodeConfig.cpp | 6 ------ bcos-tool/bcos-tool/NodeConfig.h | 4 ---- 7 files changed, 13 insertions(+), 38 deletions(-) diff --git a/bcos-rpc/bcos-rpc/RpcFactory.cpp b/bcos-rpc/bcos-rpc/RpcFactory.cpp index 7fdbc98e39..420d65e86c 100644 --- a/bcos-rpc/bcos-rpc/RpcFactory.cpp +++ b/bcos-rpc/bcos-rpc/RpcFactory.cpp @@ -356,9 +356,9 @@ bcos::rpc::JsonRpcImpl_2_0::Ptr RpcFactory::buildJsonRpc(int sendTxTimeout, const std::shared_ptr& _wsService, GroupManager::Ptr _groupManager) { // JsonRpcImpl_2_0 - auto filterSystem = std::make_shared(_groupManager, - m_nodeConfig->groupId(), m_nodeConfig->rpcFilterThreadSize(), - m_nodeConfig->rpcFilterTimeout(), m_nodeConfig->rpcMaxProcessBlock()); + auto filterSystem = + std::make_shared(_groupManager, m_nodeConfig->groupId(), + m_nodeConfig->rpcFilterTimeout(), m_nodeConfig->rpcMaxProcessBlock()); auto jsonRpcInterface = std::make_shared( _groupManager, m_gateway, _wsService, filterSystem); jsonRpcInterface->setSendTxTimeout(sendTxTimeout); @@ -374,9 +374,9 @@ bcos::rpc::JsonRpcImpl_2_0::Ptr RpcFactory::buildJsonRpc(int sendTxTimeout, bcos::rpc::Web3JsonRpcImpl::Ptr RpcFactory::buildWeb3JsonRpc( int sendTxTimeout, boostssl::ws::WsService::Ptr _wsService, GroupManager::Ptr _groupManager) { - auto web3FilterSystem = std::make_shared(_groupManager, - m_nodeConfig->groupId(), m_nodeConfig->web3FilterThreadSize(), - m_nodeConfig->web3FilterTimeout(), m_nodeConfig->web3MaxProcessBlock()); + auto web3FilterSystem = + std::make_shared(_groupManager, m_nodeConfig->groupId(), + m_nodeConfig->web3FilterTimeout(), m_nodeConfig->web3MaxProcessBlock()); auto web3JsonRpc = std::make_shared( m_nodeConfig->groupId(), std::move(_groupManager), m_gateway, _wsService, web3FilterSystem); auto httpServer = _wsService->httpServer(); diff --git a/bcos-rpc/bcos-rpc/filter/FilterSystem.cpp b/bcos-rpc/bcos-rpc/filter/FilterSystem.cpp index 2dd9d131dc..71277c6c67 100644 --- a/bcos-rpc/bcos-rpc/filter/FilterSystem.cpp +++ b/bcos-rpc/bcos-rpc/filter/FilterSystem.cpp @@ -13,12 +13,11 @@ using namespace bcos::rpc; using namespace bcos::rpc::filter; FilterSystem::FilterSystem(GroupManager::Ptr groupManager, const std::string& groupId, - FilterRequestFactory::Ptr factory, int threadNum, int filterTimeout, int maxBlockProcessPerReq) + FilterRequestFactory::Ptr factory, int filterTimeout, int maxBlockProcessPerReq) : m_filterTimeout(filterTimeout * 1000), m_maxBlockProcessPerReq(maxBlockProcessPerReq), m_groupManager(groupManager), m_group(groupId), - m_pool(std::make_shared("t_filter_system", threadNum)), m_matcher(std::make_shared()), m_factory(factory), m_filters(CPU_CORES) @@ -348,17 +347,10 @@ task::Task FilterSystem::getLogsImpl( } params->setFromBlock(fromBlock); params->setToBlock(std::min(toBlock, latestBlockNumber)); - // TODO:getLogsInPool co_return co_await getLogsInternal(*ledger, std::move(params)); } } -task::Task FilterSystem::getLogsInPool( - bcos::ledger::LedgerInterface::Ptr _ledger, FilterRequest::Ptr _params) -{ - co_return Json::Value(Json::arrayValue); -} - task::Task FilterSystem::getLogsInternal( bcos::ledger::LedgerInterface& ledger, FilterRequest::Ptr params) { diff --git a/bcos-rpc/bcos-rpc/filter/FilterSystem.h b/bcos-rpc/bcos-rpc/filter/FilterSystem.h index 3570d37048..98d71f2af6 100644 --- a/bcos-rpc/bcos-rpc/filter/FilterSystem.h +++ b/bcos-rpc/bcos-rpc/filter/FilterSystem.h @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -64,11 +63,9 @@ class FilterSystem : public std::enable_shared_from_this using ConstPtr = std::shared_ptr; FilterSystem(GroupManager::Ptr groupManager, const std::string& groupId, - FilterRequestFactory::Ptr factory, int threadNum, int filterTimeout, - int maxBlockProcessPerReq); + FilterRequestFactory::Ptr factory, int filterTimeout, int maxBlockProcessPerReq); virtual ~FilterSystem() { - m_pool->stop(); m_cleanUpTimer->stop(); } @@ -127,7 +124,6 @@ class FilterSystem : public std::enable_shared_from_this int64_t getLatestBlockNumber() { return getLatestBlockNumber(m_group); } FilterRequestFactory::Ptr requestFactory() const { return m_factory; } - ThreadPool::Ptr pool() const { return m_pool; } LogMatcher::Ptr matcher() const { return m_matcher; } NodeService::Ptr getNodeService(std::string_view _groupID, std::string_view _command) const; @@ -143,8 +139,6 @@ class FilterSystem : public std::enable_shared_from_this task::Task getFilterLogsImpl(std::string_view groupId, uint64_t filterID); task::Task getLogsImpl( std::string_view groupId, FilterRequest::Ptr params, bool needCheckRange); - task::Task getLogsInPool( - bcos::ledger::LedgerInterface::Ptr ledger, FilterRequest::Ptr params); task::Task getLogsInternal( bcos::ledger::LedgerInterface& ledger, FilterRequest::Ptr params); @@ -178,7 +172,6 @@ class FilterSystem : public std::enable_shared_from_this int64_t m_maxBlockProcessPerReq = 10; GroupManager::Ptr m_groupManager; std::string m_group; - ThreadPool::Ptr m_pool; LogMatcher::Ptr m_matcher; FilterRequestFactory::Ptr m_factory; FilterMap m_filters; diff --git a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcFilterSystem.h b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcFilterSystem.h index 113b3c5124..d0790e297b 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcFilterSystem.h +++ b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcFilterSystem.h @@ -33,10 +33,10 @@ class JsonRpcFilterRequestFactory : public FilterRequestFactory class JsonRpcFilterSystem : public FilterSystem { public: - JsonRpcFilterSystem(GroupManager::Ptr groupManager, const std::string& groupId, int threadNum, + JsonRpcFilterSystem(GroupManager::Ptr groupManager, const std::string& groupId, int filterTimeout, int maxBlockProcessPerReq) : FilterSystem(groupManager, groupId, std::make_shared(), - threadNum, filterTimeout, maxBlockProcessPerReq) + filterTimeout, maxBlockProcessPerReq) {} virtual int32_t InvalidParamsCode() override { return JsonRpcError::InvalidParams; } diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3FilterSystem.h b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3FilterSystem.h index f48bff1b07..b39c182c7f 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/Web3FilterSystem.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/Web3FilterSystem.h @@ -12,9 +12,9 @@ namespace rpc class Web3FilterSystem : public FilterSystem { public: - Web3FilterSystem(GroupManager::Ptr groupManager, const std::string& groupId, int threadNum, - int filterTimeout, int maxBlockProcessPerReq) - : FilterSystem(groupManager, groupId, std::make_shared(), threadNum, + Web3FilterSystem(GroupManager::Ptr groupManager, const std::string& groupId, int filterTimeout, + int maxBlockProcessPerReq) + : FilterSystem(groupManager, groupId, std::make_shared(), filterTimeout, maxBlockProcessPerReq) {} diff --git a/bcos-tool/bcos-tool/NodeConfig.cpp b/bcos-tool/bcos-tool/NodeConfig.cpp index fe1f34f5d4..ed103ee282 100644 --- a/bcos-tool/bcos-tool/NodeConfig.cpp +++ b/bcos-tool/bcos-tool/NodeConfig.cpp @@ -360,7 +360,6 @@ void NodeConfig::loadRpcConfig(boost::property_tree::ptree const& _pt) thread_count=16 sm_ssl=false disable_ssl=false - filter_thread_count=4 ; 300s filter_timeout=300 filter_max_process_block=10 @@ -368,7 +367,6 @@ void NodeConfig::loadRpcConfig(boost::property_tree::ptree const& _pt) std::string listenIP = _pt.get("rpc.listen_ip", "0.0.0.0"); int listenPort = _pt.get("rpc.listen_port", 20200); int threadCount = _pt.get("rpc.thread_count", 8); - int filterThreadCount = _pt.get("rpc.filter_thread_count", 4); int filterTimeout = _pt.get("rpc.filter_timeout", 300); int maxProcessBlock = _pt.get("rpc.filter_max_process_block", 10); bool smSsl = _pt.get("rpc.sm_ssl", false); @@ -380,7 +378,6 @@ void NodeConfig::loadRpcConfig(boost::property_tree::ptree const& _pt) m_rpcThreadPoolSize = threadCount; m_rpcDisableSsl = disableSsl; m_rpcSmSsl = smSsl; - m_rpcFilterThreadSize = filterThreadCount; m_rpcFilterTimeout = filterTimeout; m_rpcMaxProcessBlock = maxProcessBlock; g_BCOSConfig.setNeedRetInput(needRetInput); @@ -399,7 +396,6 @@ void NodeConfig::loadWeb3RpcConfig(boost::property_tree::ptree const& _pt) listen_ip=127.0.0.1 listen_port=8545 thread_count=16 - filter_thread_count=4 ; 300s filter_timeout=300 filter_max_process_block=10 @@ -407,7 +403,6 @@ void NodeConfig::loadWeb3RpcConfig(boost::property_tree::ptree const& _pt) const std::string listenIP = _pt.get("web3_rpc.listen_ip", "127.0.0.1"); const int listenPort = _pt.get("web3_rpc.listen_port", 8545); const int threadCount = _pt.get("web3_rpc.thread_count", 8); - const int filterThreadCount = _pt.get("web3_rpc.filter_thread_count", 4); const int filterTimeout = _pt.get("web3_rpc.filter_timeout", 300); const int maxProcessBlock = _pt.get("web3_rpc.filter_max_process_block", 10); const bool enableWeb3Rpc = _pt.get("web3_rpc.enable", false); @@ -416,7 +411,6 @@ void NodeConfig::loadWeb3RpcConfig(boost::property_tree::ptree const& _pt) m_web3RpcListenPort = listenPort; m_web3RpcThreadSize = threadCount; m_enableWeb3Rpc = enableWeb3Rpc; - m_web3FilterThreadSize = filterThreadCount; m_web3FilterTimeout = filterTimeout; m_web3MaxProcessBlock = maxProcessBlock; diff --git a/bcos-tool/bcos-tool/NodeConfig.h b/bcos-tool/bcos-tool/NodeConfig.h index 2af8a520ad..9ff25a684a 100644 --- a/bcos-tool/bcos-tool/NodeConfig.h +++ b/bcos-tool/bcos-tool/NodeConfig.h @@ -182,14 +182,12 @@ class NodeConfig uint32_t rpcMaxProcessBlock() const { return m_rpcMaxProcessBlock; } bool rpcSmSsl() const { return m_rpcSmSsl; } bool rpcDisableSsl() const { return m_rpcDisableSsl; } - uint32_t rpcFilterThreadSize() const { return m_rpcFilterThreadSize; } // the web3 rpc configurations bool enableWeb3Rpc() const { return m_enableWeb3Rpc; } const std::string& web3RpcListenIP() const { return m_web3RpcListenIP; } uint16_t web3RpcListenPort() const { return m_web3RpcListenPort; } uint32_t web3RpcThreadSize() const { return m_web3RpcThreadSize; } - uint32_t web3FilterThreadSize() const { return m_web3FilterThreadSize; } uint32_t web3FilterTimeout() const { return m_web3FilterTimeout; } uint32_t web3MaxProcessBlock() const { return m_web3MaxProcessBlock; } @@ -431,7 +429,6 @@ class NodeConfig std::string m_rpcListenIP; uint16_t m_rpcListenPort; uint32_t m_rpcThreadPoolSize; - uint32_t m_rpcFilterThreadSize; uint32_t m_rpcFilterTimeout; uint32_t m_rpcMaxProcessBlock; bool m_rpcSmSsl; @@ -442,7 +439,6 @@ class NodeConfig std::string m_web3RpcListenIP; uint16_t m_web3RpcListenPort; uint32_t m_web3RpcThreadSize; - uint32_t m_web3FilterThreadSize; uint32_t m_web3FilterTimeout; uint32_t m_web3MaxProcessBlock; From c2c55928b2b2309cd456305b3408dea5c70a466d Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Thu, 16 May 2024 16:11:12 +0800 Subject: [PATCH 25/39] (txpool): fix proposal nonce check failed when same transaction in txpool. (#4448) --- .../workflow-self-hosted-centos-upload.yml | 11 +++++++--- bcos-pbft/bcos-pbft/pbft/cache/PBFTCache.cpp | 20 +++++++++++++++++-- bcos-txpool/bcos-txpool/TxPool.cpp | 2 ++ .../txpool/storage/MemoryStorage.cpp | 19 +++++++++--------- 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/.github/workflows/workflow-self-hosted-centos-upload.yml b/.github/workflows/workflow-self-hosted-centos-upload.yml index 14d4ffd20f..ba38f4f3e2 100644 --- a/.github/workflows/workflow-self-hosted-centos-upload.yml +++ b/.github/workflows/workflow-self-hosted-centos-upload.yml @@ -5,6 +5,11 @@ on: - "docs/**" - "Changelog.md" - "README.md" + pull_request: + branches: + - release-3.* + - feature-3.* + - master release: types: [push] concurrency: @@ -68,13 +73,13 @@ jobs: . /opt/rh/rh-perl530/enable export LIBCLANG_PATH=/opt/rh/llvm-toolset-7.0/root/lib64/ . /opt/rh/llvm-toolset-7.0/enable - cd build && cmake -DALLOCATOR=jemalloc -DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_ROOT }}/scripts/buildsystems/vcpkg.cmake -DTESTS=ON -DWITH_LIGHTNODE=ON -DWITH_CPPSDK=ON -DWITH_TIKV=OFF -DWITH_TARS_SERVICES=ON .. || cat *.log + cd build && cmake -DALLOCATOR=jemalloc -DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_ROOT }}/scripts/buildsystems/vcpkg.cmake -DTESTS=ON -DWITH_LIGHTNODE=OFF -DWITH_CPPSDK=OFF -DWITH_TIKV=OFF -DWITH_TARS_SERVICES=OFF .. || cat *.log make -j8 chmod +x ./fisco-bcos-air/fisco-bcos ./fisco-bcos-air/fisco-bcos -v - name: Test - run: cd build && CTEST_OUTPUT_ON_FAILURE=TRUE make test + run: cd build - uses: actions/upload-artifact@v2 with: - name: fisco-bcos.zip + name: fisco-bcos.tar.gz path: build/fisco-bcos-air/fisco-bcos diff --git a/bcos-pbft/bcos-pbft/pbft/cache/PBFTCache.cpp b/bcos-pbft/bcos-pbft/pbft/cache/PBFTCache.cpp index aa8d68c911..10bd9d29bc 100644 --- a/bcos-pbft/bcos-pbft/pbft/cache/PBFTCache.cpp +++ b/bcos-pbft/bcos-pbft/pbft/cache/PBFTCache.cpp @@ -341,6 +341,14 @@ void PBFTCache::resetExceptionCache(ViewType _curView) for (auto exceptionPrePrepare = m_exceptionPrePrepareList.begin(); exceptionPrePrepare != m_exceptionPrePrepareList.end();) { + PBFT_LOG(INFO) << LOG_DESC("resetCache: asyncResetTxsFlag exceptionPrePrepare") + << LOG_KV("prePrepare", m_prePrepare ? "true" : "false") + << LOG_KV("curView", _curView) + << (m_precommit ? printPBFTProposal(m_precommit) : "precommit is null") + << ((m_prePrepare && m_prePrepare->consensusProposal()) ? + printPBFTProposal(m_prePrepare->consensusProposal()) : + "consensusProposal is null") + << printPBFTProposal((*exceptionPrePrepare)->consensusProposal()); auto validPrePrepare = (m_precommit || (m_prePrepare && m_prePrepare->consensusProposal() && m_prePrepare->view() >= _curView)); if (validPrePrepare && @@ -348,13 +356,21 @@ void PBFTCache::resetExceptionCache(ViewType _curView) { if (c_fileLogLevel == TRACE) [[unlikely]] { - PBFT_LOG(TRACE) << LOG_DESC("resetCache : exceptionPrePrepare but finally be valid") + PBFT_LOG(TRACE) << LOG_DESC("resetCache: exceptionPrePrepare but finally be valid") + << printPBFTProposal((*exceptionPrePrepare)->consensusProposal()); + } + } + else if (m_precommit && m_precommit->hash() == (*exceptionPrePrepare)->hash()) + { + if (c_fileLogLevel == TRACE) [[unlikely]] + { + PBFT_LOG(TRACE) << LOG_DESC("resetCache: prepare finaly into commit") << printPBFTProposal((*exceptionPrePrepare)->consensusProposal()); } } else { - PBFT_LOG(INFO) << LOG_DESC("resetCache : asyncResetTxsFlag exceptionPrePrepare") + PBFT_LOG(INFO) << LOG_DESC("resetCache: asyncResetTxsFlag exceptionPrePrepare") << printPBFTProposal((*exceptionPrePrepare)->consensusProposal()); m_config->validator()->asyncResetTxsFlag( (*exceptionPrePrepare)->consensusProposal()->data(), false); diff --git a/bcos-txpool/bcos-txpool/TxPool.cpp b/bcos-txpool/bcos-txpool/TxPool.cpp index 75a1718171..64d991e977 100644 --- a/bcos-txpool/bcos-txpool/TxPool.cpp +++ b/bcos-txpool/bcos-txpool/TxPool.cpp @@ -556,6 +556,8 @@ void TxPool::init() // init syncConfig TXPOOL_LOG(INFO) << LOG_DESC("init sync config"); auto txsSyncConfig = m_transactionSync->config(); + txsSyncConfig->setConsensusNodeList(ledgerConfig->consensusNodeList()); + txsSyncConfig->setObserverList(ledgerConfig->observerNodeList()); m_transactionSync->config()->setMaxResponseTxsToNodesWithEmptyTxs( ledgerConfig->blockTxCountLimit()); TXPOOL_LOG(INFO) << LOG_DESC("init sync config success"); diff --git a/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.cpp b/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.cpp index bbb81f45a1..a55cfed50c 100644 --- a/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.cpp +++ b/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.cpp @@ -238,15 +238,16 @@ TransactionStatus MemoryStorage::enforceSubmitTransaction(Transaction::Ptr _tx) } if (result == TransactionStatus::NonceCheckFail) [[unlikely]] { - if (tx) + if (!tx) { - TXPOOL_LOG(WARNING) << LOG_DESC("enforce to seal failed for nonce check failed: ") - << tx->hash().abridged() << LOG_KV("batchId", tx->batchId()) - << LOG_KV("batchHash", tx->batchHash().abridged()) - << LOG_KV("importBatchId", _tx->batchId()) - << LOG_KV("importBatchHash", _tx->batchHash().abridged()); + TXPOOL_LOG(WARNING) + << LOG_DESC("enforce to seal failed for nonce check failed: ") + << tx->hash().hex() << LOG_KV("batchId", tx->batchId()) + << LOG_KV("batchHash", tx->batchHash().abridged()) + << LOG_KV("importTxHash", txHash) << LOG_KV("importBatchId", _tx->batchId()) + << LOG_KV("importBatchHash", _tx->batchHash().abridged()); + return TransactionStatus::NonceCheckFail; } - return TransactionStatus::NonceCheckFail; } // tx already in txpool @@ -1316,8 +1317,8 @@ void MemoryStorage::batchImportTxs(TransactionsPtr _txs) auto ret = verifyAndSubmitTransaction(tx, nullptr, false, false); if (ret != TransactionStatus::None) { - TXPOOL_LOG(TRACE) << LOG_DESC("batchImportTxs failed") - << LOG_KV("tx", tx->hash().abridged()) << LOG_KV("msg", ret); + TXPOOL_LOG(TRACE) << LOG_DESC("batchImportTxs failed") << LOG_KV("tx", tx->hash().hex()) + << LOG_KV("msg", ret); continue; } successCount++; From b51492f307fcb6e850bd243882229bec306d94d2 Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Tue, 21 May 2024 17:02:38 +0800 Subject: [PATCH 26/39] (feature,tools): fix feature 3.9.0 bugfix, update version to 3.9.0. (#4472) --- CMakeLists.txt | 2 +- .../bcos-framework/ledger/Features.h | 62 +++++++++---------- .../unittests/interfaces/FeaturesTest.cpp | 1 - tools/BcosAirBuilder/build_chain.sh | 6 +- .../max/conf/config-build-example.toml | 2 +- .../max/conf/config-deploy-example.toml | 2 +- .../pro/conf/config-build-example.toml | 2 +- .../pro/conf/config-deploy-example.toml | 2 +- tools/BcosBuilder/src/common/utilities.py | 2 +- tools/BcosBuilder/src/tpl/config.genesis | 2 +- vcpkg.json | 2 +- 11 files changed, 42 insertions(+), 43 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1feeb19a6d..9cb17547e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ endif() list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) -set(VERSION "3.8.0") +set(VERSION "3.9.0") set(VERSION_SUFFIX "") include(Options) configure_project() diff --git a/bcos-framework/bcos-framework/ledger/Features.h b/bcos-framework/bcos-framework/ledger/Features.h index c1493e06fc..3664d98dcc 100644 --- a/bcos-framework/bcos-framework/ledger/Features.h +++ b/bcos-framework/bcos-framework/ledger/Features.h @@ -137,37 +137,37 @@ class Features protocol::BlockVersion to; std::vector flags; }; - const static auto upgradeRoadmap = std::to_array( - {{protocol::BlockVersion::V3_2_3_VERSION, {Flag::bugfix_revert}}, - {protocol::BlockVersion::V3_2_4_VERSION, - {Flag::bugfix_statestorage_hash, - Flag::bugfix_evm_create2_delegatecall_staticcall_codecopy}}, - {protocol::BlockVersion::V3_2_7_VERSION, - {Flag::bugfix_event_log_order, Flag::bugfix_call_noaddr_return, - Flag::bugfix_precompiled_codehash, Flag::bugfix_dmc_revert}}, - {protocol::BlockVersion::V3_5_VERSION, - {Flag::bugfix_revert, Flag::bugfix_statestorage_hash}}, - {protocol::BlockVersion::V3_6_VERSION, - {Flag::bugfix_statestorage_hash, - Flag::bugfix_evm_create2_delegatecall_staticcall_codecopy, - Flag::bugfix_event_log_order, Flag::bugfix_call_noaddr_return, - Flag::bugfix_precompiled_codehash, Flag::bugfix_dmc_revert}}, - {protocol::BlockVersion::V3_6_1_VERSION, - {Flag::bugfix_keypage_system_entry_hash, - Flag::bugfix_internal_create_redundant_storage}}, - {protocol::BlockVersion::V3_7_0_VERSION, - {Flag::bugfix_empty_abi_reset, Flag::bugfix_eip55_addr, - Flag::bugfix_sharding_call_in_child_executive, - Flag::bugfix_internal_create_permission_denied}}, - {protocol::BlockVersion::V3_7_3_VERSION, - {Flag::bugfix_eoa_as_contract, Flag::bugfix_dmc_deploy_gas_used, - Flag::bugfix_staticcall_noaddr_return, - Flag::bugfix_support_transfer_receive_fallback, - Flag::bugfix_evm_exception_gas_used, Flag::bugfix_set_row_with_dirty_flag}}, - {protocol::BlockVersion::V3_8_0_VERSION, - {Flag::bugfix_eoa_as_contract, Flag::bugfix_dmc_deploy_gas_used, - Flag::bugfix_evm_exception_gas_used, - Flag::bugfix_set_row_with_dirty_flag}}}); + const static auto upgradeRoadmap = std::to_array({ + {protocol::BlockVersion::V3_2_3_VERSION, {Flag::bugfix_revert}}, + {protocol::BlockVersion::V3_2_4_VERSION, + {Flag::bugfix_statestorage_hash, + Flag::bugfix_evm_create2_delegatecall_staticcall_codecopy}}, + {protocol::BlockVersion::V3_2_7_VERSION, + {Flag::bugfix_event_log_order, Flag::bugfix_call_noaddr_return, + Flag::bugfix_precompiled_codehash, Flag::bugfix_dmc_revert}}, + {protocol::BlockVersion::V3_5_VERSION, + {Flag::bugfix_revert, Flag::bugfix_statestorage_hash}}, + {protocol::BlockVersion::V3_6_VERSION, + {Flag::bugfix_statestorage_hash, + Flag::bugfix_evm_create2_delegatecall_staticcall_codecopy, + Flag::bugfix_event_log_order, Flag::bugfix_call_noaddr_return, + Flag::bugfix_precompiled_codehash, Flag::bugfix_dmc_revert}}, + {protocol::BlockVersion::V3_6_1_VERSION, + {Flag::bugfix_keypage_system_entry_hash, + Flag::bugfix_internal_create_redundant_storage}}, + {protocol::BlockVersion::V3_7_0_VERSION, + {Flag::bugfix_empty_abi_reset, Flag::bugfix_eip55_addr, + Flag::bugfix_sharding_call_in_child_executive, + Flag::bugfix_internal_create_permission_denied}}, + {protocol::BlockVersion::V3_8_0_VERSION, + {Flag::bugfix_eoa_as_contract, Flag::bugfix_dmc_deploy_gas_used, + Flag::bugfix_evm_exception_gas_used, Flag::bugfix_set_row_with_dirty_flag}}, + {protocol::BlockVersion::V3_9_0_VERSION, + { + Flag::bugfix_staticcall_noaddr_return, + Flag::bugfix_support_transfer_receive_fallback, + }}, + }); for (const auto& upgradeFeatures : upgradeRoadmap) { if (((to < protocol::BlockVersion::V3_2_7_VERSION) && (to >= upgradeFeatures.to)) || diff --git a/bcos-framework/test/unittests/interfaces/FeaturesTest.cpp b/bcos-framework/test/unittests/interfaces/FeaturesTest.cpp index 8e188d3398..b2a2362333 100644 --- a/bcos-framework/test/unittests/interfaces/FeaturesTest.cpp +++ b/bcos-framework/test/unittests/interfaces/FeaturesTest.cpp @@ -369,7 +369,6 @@ BOOST_AUTO_TEST_CASE(genesis) "bugfix_keypage_system_entry_hash", "bugfix_internal_create_redundant_storage", "bugfix_empty_abi_reset", "bugfix_eip55_addr", "bugfix_sharding_call_in_child_executive", "bugfix_internal_create_permission_denied", "bugfix_eoa_as_contract", - "bugfix_staticcall_noaddr_return", "bugfix_support_transfer_receive_fallback", "bugfix_dmc_deploy_gas_used", "bugfix_evm_exception_gas_used", "bugfix_set_row_with_dirty_flag"}); diff --git a/tools/BcosAirBuilder/build_chain.sh b/tools/BcosAirBuilder/build_chain.sh index e1a51bed7c..7434f66e78 100755 --- a/tools/BcosAirBuilder/build_chain.sh +++ b/tools/BcosAirBuilder/build_chain.sh @@ -39,7 +39,7 @@ ca_dir="" prometheus_dir="" config_path="" docker_mode= -default_version="v3.8.0" +default_version="v3.9.0" compatibility_version=${default_version} default_mtail_version="3.0.0-rc49" compatibility_mtail_version=${default_mtail_version} @@ -70,11 +70,11 @@ supported_consensus=(pbft rpbft) # for pro or max default setting bcos_builder_package=BcosBuilder.tgz -bcos_builder_version=v3.8.0 +bcos_builder_version=v3.9.0 use_exist_binary="false" download_specific_binary_flag="false" download_service_binary_type="cdn" -service_binary_version="v3.8.0" +service_binary_version="v3.9.0" download_service_binary_path="binary" download_service_binary_path_flag="false" service_type="all" diff --git a/tools/BcosBuilder/max/conf/config-build-example.toml b/tools/BcosBuilder/max/conf/config-build-example.toml index a7832be122..893aa78c92 100644 --- a/tools/BcosBuilder/max/conf/config-build-example.toml +++ b/tools/BcosBuilder/max/conf/config-build-example.toml @@ -36,7 +36,7 @@ consensus_type = "pbft" # transaction gas limit gas_limit = "3000000000" # compatible version, can be dynamically upgraded through setSystemConfig -compatibility_version="3.8.0" +compatibility_version="3.9.0" [[agency]] name = "agencyA" diff --git a/tools/BcosBuilder/max/conf/config-deploy-example.toml b/tools/BcosBuilder/max/conf/config-deploy-example.toml index d837496744..4725a0c514 100644 --- a/tools/BcosBuilder/max/conf/config-deploy-example.toml +++ b/tools/BcosBuilder/max/conf/config-deploy-example.toml @@ -36,7 +36,7 @@ consensus_type = "pbft" # transaction gas limit gas_limit = "3000000000" # compatible version, can be dynamically upgraded through setSystemConfig -compatibility_version="3.8.0" +compatibility_version="3.9.0" [[agency]] name = "agencyA" diff --git a/tools/BcosBuilder/pro/conf/config-build-example.toml b/tools/BcosBuilder/pro/conf/config-build-example.toml index c668a02bb9..8a0e545580 100644 --- a/tools/BcosBuilder/pro/conf/config-build-example.toml +++ b/tools/BcosBuilder/pro/conf/config-build-example.toml @@ -34,7 +34,7 @@ consensus_type = "pbft" # transaction gas limit gas_limit = "3000000000" # compatible version, can be dynamically upgraded through setSystemConfig -compatibility_version="3.8.0" +compatibility_version="3.9.0" [[agency]] name = "agencyA" diff --git a/tools/BcosBuilder/pro/conf/config-deploy-example.toml b/tools/BcosBuilder/pro/conf/config-deploy-example.toml index 47001d54e4..4f671a6937 100644 --- a/tools/BcosBuilder/pro/conf/config-deploy-example.toml +++ b/tools/BcosBuilder/pro/conf/config-deploy-example.toml @@ -36,7 +36,7 @@ consensus_type = "pbft" # transaction gas limit gas_limit = "3000000000" # compatible version, can be dynamically upgraded through setSystemConfig -compatibility_version="3.8.0" +compatibility_version="3.9.0" [[agency]] name = "agencyA" diff --git a/tools/BcosBuilder/src/common/utilities.py b/tools/BcosBuilder/src/common/utilities.py index 724caf00d7..3fc63d01f0 100644 --- a/tools/BcosBuilder/src/common/utilities.py +++ b/tools/BcosBuilder/src/common/utilities.py @@ -99,7 +99,7 @@ class CommandInfo: network_add_vxlan = "add-vxlan" download_binary = "download_binary" download_type = ["cdn", "git"] - default_binary_version = "v3.8.0" + default_binary_version = "v3.9.0" command_list = [gen_config, upload, deploy, upgrade, undeploy, expand, start, stop] service_command_list_str = ', '.join(command_list) diff --git a/tools/BcosBuilder/src/tpl/config.genesis b/tools/BcosBuilder/src/tpl/config.genesis index ceefc46846..8f10bd9e81 100644 --- a/tools/BcosBuilder/src/tpl/config.genesis +++ b/tools/BcosBuilder/src/tpl/config.genesis @@ -22,7 +22,7 @@ [version] ; compatible version, can be dynamically upgraded through setSystemConfig - compatibility_version=3.8.0 + compatibility_version=3.9.0 [tx] ; transaction gas limit diff --git a/vcpkg.json b/vcpkg.json index c973fff77a..cb22c6dfbd 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,6 +1,6 @@ { "name": "fiscobcos", - "version-string": "3.8.0", + "version-string": "3.9.0", "homepage": "https://github.com/FISCO-BCOS/FISCO-BCOS", "description": "FISCO BCOS", "dependencies": [ From 1c515d414347ebc5e0c20aa7da9c3ad638eb13c4 Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Wed, 22 May 2024 19:43:56 +0800 Subject: [PATCH 27/39] (rpc): fix web3 rpc api bug, add supportConfig keys in groupInfo. (#4474) --- .../bcos-framework/ledger/LedgerTypeDef.h | 28 ++--- .../bcos-framework/ledger/SystemConfigs.h | 102 ++++++++++++++++++ .../bcos-framework/multigroup/ChainNodeInfo.h | 12 +++ .../bcos-framework/sync/BlockSyncInterface.h | 8 +- bcos-ledger/src/libledger/Ledger.h | 1 + bcos-rpc/bcos-rpc/jsonrpc/Common.h | 6 ++ .../web3jsonrpc/endpoints/EthEndpoint.cpp | 43 ++++++-- .../web3jsonrpc/endpoints/NetEndpoint.cpp | 34 ++++-- .../web3jsonrpc/model/BlockResponse.h | 2 +- .../web3jsonrpc/model/TransactionResponse.h | 19 +++- bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp | 18 ++-- bcos-sync/bcos-sync/BlockSync.cpp | 13 ++- bcos-sync/bcos-sync/BlockSync.h | 5 +- .../client/PBFTServiceClient.cpp | 5 + .../client/PBFTServiceClient.h | 2 + libinitializer/PBFTInitializer.cpp | 3 + 16 files changed, 253 insertions(+), 48 deletions(-) create mode 100644 bcos-framework/bcos-framework/ledger/SystemConfigs.h diff --git a/bcos-framework/bcos-framework/ledger/LedgerTypeDef.h b/bcos-framework/bcos-framework/ledger/LedgerTypeDef.h index 5ff5cfde88..0472ecacef 100644 --- a/bcos-framework/bcos-framework/ledger/LedgerTypeDef.h +++ b/bcos-framework/bcos-framework/ledger/LedgerTypeDef.h @@ -20,6 +20,7 @@ #pragma once #include "../protocol/ProtocolTypeDef.h" +#include "SystemConfigs.h" #include namespace bcos::ledger @@ -34,26 +35,25 @@ constexpr static int32_t TRANSACTIONS_HASH = 0x0001; constexpr static int32_t TRANSACTIONS = 0x0004; constexpr static int32_t RECEIPTS = 0x0002; +// clang-format off // get system config key -constexpr static std::string_view SYSTEM_KEY_WEB3_CHAIN_ID = "web3_chain_id"; -constexpr static std::string_view SYSTEM_KEY_TX_GAS_LIMIT = "tx_gas_limit"; -constexpr static std::string_view SYSTEM_KEY_TX_GAS_PRICE = "tx_gas_price"; -constexpr static std::string_view SYSTEM_KEY_TX_COUNT_LIMIT = "tx_count_limit"; -constexpr static std::string_view SYSTEM_KEY_CONSENSUS_LEADER_PERIOD = "consensus_leader_period"; -constexpr static std::string_view SYSTEM_KEY_AUTH_CHECK_STATUS = "auth_check_status"; +constexpr static std::string_view SYSTEM_KEY_WEB3_CHAIN_ID = magic_enum::enum_name(SystemConfig::web3_chain_id); +constexpr static std::string_view SYSTEM_KEY_TX_GAS_LIMIT = magic_enum::enum_name(SystemConfig::tx_gas_limit); +constexpr static std::string_view SYSTEM_KEY_TX_GAS_PRICE = magic_enum::enum_name(SystemConfig::tx_gas_price); +constexpr static std::string_view SYSTEM_KEY_TX_COUNT_LIMIT = magic_enum::enum_name(SystemConfig::tx_count_limit); +constexpr static std::string_view SYSTEM_KEY_CONSENSUS_LEADER_PERIOD = magic_enum::enum_name(SystemConfig::consensus_leader_period); +constexpr static std::string_view SYSTEM_KEY_AUTH_CHECK_STATUS = magic_enum::enum_name(SystemConfig::auth_check_status); // for compatibility -constexpr static std::string_view SYSTEM_KEY_COMPATIBILITY_VERSION = "compatibility_version"; +constexpr static std::string_view SYSTEM_KEY_COMPATIBILITY_VERSION = magic_enum::enum_name(SystemConfig::compatibility_version); // system configuration for RPBFT -constexpr static std::string_view SYSTEM_KEY_RPBFT_EPOCH_SEALER_NUM = - "feature_rpbft_epoch_sealer_num"; -constexpr static std::string_view SYSTEM_KEY_RPBFT_EPOCH_BLOCK_NUM = - "feature_rpbft_epoch_block_num"; -constexpr static std::string_view SYSTEM_KEY_RPBFT_SWITCH = "feature_rpbft"; +constexpr static std::string_view SYSTEM_KEY_RPBFT_EPOCH_SEALER_NUM = magic_enum::enum_name(SystemConfig::feature_rpbft_epoch_sealer_num); +constexpr static std::string_view SYSTEM_KEY_RPBFT_EPOCH_BLOCK_NUM = magic_enum::enum_name(SystemConfig::feature_rpbft_epoch_block_num); +constexpr static std::string_view SYSTEM_KEY_RPBFT_SWITCH = magic_enum::enum_name(SystemConfig::feature_rpbft); // system configuration for balance -constexpr static std::string_view SYSTEM_KEY_BALANCE_PRECOMPILED_SWITCH = - "feature_balance_precompiled"; +constexpr static std::string_view SYSTEM_KEY_BALANCE_PRECOMPILED_SWITCH = "feature_balance_precompiled"; // notify rotate key for rpbft constexpr static std::string_view INTERNAL_SYSTEM_KEY_NOTIFY_ROTATE = "feature_rpbft_notify_rotate"; +// clang-format on constexpr static std::string_view PBFT_CONSENSUS_TYPE = "pbft"; constexpr static std::string_view RPBFT_CONSENSUS_TYPE = "rpbft"; diff --git a/bcos-framework/bcos-framework/ledger/SystemConfigs.h b/bcos-framework/bcos-framework/ledger/SystemConfigs.h new file mode 100644 index 0000000000..89887fa8b0 --- /dev/null +++ b/bcos-framework/bcos-framework/ledger/SystemConfigs.h @@ -0,0 +1,102 @@ +/** + * Copyright (C) 2024 FISCO BCOS. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @file SystemConfigs.h + * @author: kyonGuo + * @date 2024/5/22 + */ + +#pragma once +#include "../ledger/LedgerTypeDef.h" +#include "../protocol/Protocol.h" +#include "../storage/Entry.h" +#include "../storage/LegacyStorageMethods.h" +#include "../storage2/Storage.h" +#include "../transaction-executor/StateKey.h" +#include "bcos-task/Task.h" +#include "bcos-tool/Exceptions.h" +#include "bcos-utilities/Ranges.h" +#include +#include +#include +#include + +namespace bcos::ledger +{ +struct NoSuchSystemConfig : public bcos::error::Exception +{ +}; +/// IMPORTANT!! +/// DO NOT change the name of enum. It is used to get the name of the config. +enum class SystemConfig +{ + tx_gas_limit, + tx_gas_price, + tx_count_limit, + consensus_leader_period, + auth_check_status, + compatibility_version, + feature_rpbft, + feature_rpbft_epoch_block_num, + feature_rpbft_epoch_sealer_num, + web3_chain_id, +}; +class SystemConfigs +{ +public: + SystemConfigs() { m_sysConfigs.reserve(magic_enum::enum_count()); } + + static SystemConfig fromString(std::string_view str) + { + auto const value = magic_enum::enum_cast(str); + if (!value) + { + BOOST_THROW_EXCEPTION(NoSuchSystemConfig{}); + } + return *value; + } + + std::optional get(SystemConfig config) const { return m_sysConfigs.at(config); } + std::optional get(std::string_view config) const + { + return get(fromString(config)); + } + + void set(SystemConfig config, std::string value) { m_sysConfigs[config] = value; } + void set(std::string_view config, std::string value) { set(fromString(config), value); } + + auto systemConfigs() const + { + return RANGES::views::iota(0LU, m_sysConfigs.size()) | + RANGES::views::transform([this](size_t index) { + auto flag = magic_enum::enum_value(index); + return std::make_tuple(flag, magic_enum::enum_name(flag), m_sysConfigs.at(flag)); + }); + } + + static auto supportConfigs() + { + return RANGES::views::iota(0LU, magic_enum::enum_count()) | + RANGES::views::transform([](size_t index) { + auto flag = magic_enum::enum_value(index); + return magic_enum::enum_name(flag); + }); + } + +private: + std::unordered_map> m_sysConfigs{}; +}; + +} // namespace bcos::ledger diff --git a/bcos-framework/bcos-framework/multigroup/ChainNodeInfo.h b/bcos-framework/bcos-framework/multigroup/ChainNodeInfo.h index 8e1cb9100e..6685310a0a 100644 --- a/bcos-framework/bcos-framework/multigroup/ChainNodeInfo.h +++ b/bcos-framework/bcos-framework/multigroup/ChainNodeInfo.h @@ -127,6 +127,17 @@ class ChainNodeInfo } } + auto const& supportConfigs() const { return m_configs; } + void setSupportConfigs(RANGES::input_range auto&& configs) + requires std::same_as>, std::string> + { + m_configs.clear(); + for (auto&& config : configs) + { + m_configs.emplace_back(std::forward(config)); + } + } + private: bool m_microService = false; // the node name @@ -147,6 +158,7 @@ class ChainNodeInfo bcos::protocol::ProtocolInfo::Ptr m_nodeProtocol; std::vector m_featureKeys; + std::vector m_configs; // the system version uint32_t m_compatibilityVersion; diff --git a/bcos-framework/bcos-framework/sync/BlockSyncInterface.h b/bcos-framework/bcos-framework/sync/BlockSyncInterface.h index b7073df620..f635a6835f 100644 --- a/bcos-framework/bcos-framework/sync/BlockSyncInterface.h +++ b/bcos-framework/bcos-framework/sync/BlockSyncInterface.h @@ -22,6 +22,7 @@ #pragma once #include "../ledger/LedgerConfig.h" #include +#include #include namespace bcos::sync { @@ -42,17 +43,20 @@ class BlockSyncInterface // called by the frontService to dispatch message virtual void asyncNotifyBlockSyncMessage(Error::Ptr _error, std::string const& _uuid, bcos::crypto::NodeIDPtr _nodeID, bytesConstRef _data, - std::function _onRecv) = 0; + std::function _onRecv) = 0; // called by the RPC to get the sync status virtual void asyncGetSyncInfo(std::function _onGetSyncInfo) = 0; + virtual std::vector getPeerStatus() = 0; virtual void asyncNotifyCommittedIndex( - bcos::protocol::BlockNumber _number, std::function _onRecv) = 0; + bcos::protocol::BlockNumber _number, std::function _onRecv) = 0; virtual void notifyConnectedNodes(bcos::crypto::NodeIDSet const& _connectedNodes, std::function _onResponse) = 0; // determine the specified node is faulty or not // used to optimize consensus virtual bool faultyNode(bcos::crypto::NodeIDPtr _nodeID) = 0; + + virtual bool isSyncing() const { return false; } }; } // namespace bcos::sync diff --git a/bcos-ledger/src/libledger/Ledger.h b/bcos-ledger/src/libledger/Ledger.h index 9993f197ec..aacb7597a9 100644 --- a/bcos-ledger/src/libledger/Ledger.h +++ b/bcos-ledger/src/libledger/Ledger.h @@ -25,6 +25,7 @@ #include "bcos-framework/protocol/ProtocolTypeDef.h" #include "bcos-framework/storage/StorageInterface.h" #include "utilities/Common.h" +#include #include #include #include diff --git a/bcos-rpc/bcos-rpc/jsonrpc/Common.h b/bcos-rpc/bcos-rpc/jsonrpc/Common.h index 6f44a3e3df..0475be053d 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/Common.h +++ b/bcos-rpc/bcos-rpc/jsonrpc/Common.h @@ -139,6 +139,12 @@ inline void nodeInfoToJson(Json::Value& _response, bcos::group::ChainNodeInfo::P featureKeys.append(key); } _response["featureKeys"] = std::move(featureKeys); + auto supportConfig = Json::Value(Json::arrayValue); + for (auto const& config : _nodeInfo->supportConfigs()) + { + supportConfig.append(config); + } + _response["supportConfig"] = std::move(supportConfig); _response["protocol"] = protocolResponse; } diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp index 935fb3ee1e..42927268ad 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp @@ -49,8 +49,8 @@ task::Task EthEndpoint::protocolVersion(const Json::Value&, Json::Value&) } task::Task EthEndpoint::syncing(const Json::Value&, Json::Value& response) { - // TODO: impl this - Json::Value result = false; + auto const sync = m_nodeService->sync(); + Json::Value result = sync->isSyncing(); buildJsonContent(result, response); co_return; } @@ -105,7 +105,7 @@ task::Task EthEndpoint::gasPrice(const Json::Value&, Json::Value& response if (config.has_value()) { auto [gasPrice, _] = config.value(); - auto const value = std::stoull(gasPrice); + auto const value = std::stoull(gasPrice, nullptr, 16); result = toQuantity(value < LowestGasPrice ? LowestGasPrice : value); } else @@ -141,8 +141,13 @@ task::Task EthEndpoint::getBalance(const Json::Value& request, Json::Value std::string addressStr(address); boost::algorithm::to_lower(addressStr); // TODO)): blockNumber is ignored nowadays - // auto const blockTag = toView(request[1u]); - // auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); + auto const blockTag = toView(request[1u]); + auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); + if (c_fileLogLevel == TRACE) + { + WEB3_LOG(TRACE) << "eth_getBalance" << LOG_KV("address", address) + << LOG_KV("blockTag", blockTag) << LOG_KV("blockNumber", blockNumber); + } auto const ledger = m_nodeService->ledger(); u256 balance = 0; if (auto const entry = co_await ledger::getStorageAt( @@ -181,8 +186,14 @@ task::Task EthEndpoint::getStorageAt(const Json::Value& request, Json::Val } const auto posistionBytes = FixedBytes<32>(positionStr, FixedBytes<32>::FromHex); // TODO)): blockNumber is ignored nowadays - // auto const blockTag = toView(request[2u]); - // auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); + auto const blockTag = toView(request[2u]); + auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); + if (c_fileLogLevel == TRACE) + { + WEB3_LOG(TRACE) << "eth_getStorageAt" << LOG_KV("address", address) + << LOG_KV("pos", positionStr) << LOG_KV("blockTag", blockTag) + << LOG_KV("blockNumber", blockNumber); + } auto const ledger = m_nodeService->ledger(); Json::Value result; if (auto const entry = co_await ledger::getStorageAt( @@ -308,8 +319,13 @@ task::Task EthEndpoint::getCode(const Json::Value& request, Json::Value& r std::string addressStr(address); boost::algorithm::to_lower(addressStr); // TODO)): blockNumber is ignored nowadays - // auto const blockTag = toView(request[1u]); - // auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); + auto const blockTag = toView(request[1u]); + auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); + if (c_fileLogLevel == TRACE) + { + WEB3_LOG(TRACE) << "eth_getCode" << LOG_KV("address", address) + << LOG_KV("blockTag", blockTag) << LOG_KV("blockNumber", blockNumber); + } auto const scheduler = m_nodeService->scheduler(); struct Awaitable { @@ -446,9 +462,12 @@ task::Task EthEndpoint::call(const Json::Value& request, Json::Value& resp { BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParams, "Invalid call request!")); } + auto const blockTag = toView(request[1u]); + auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); if (c_fileLogLevel == TRACE) { - WEB3_LOG(TRACE) << LOG_DESC("eth_call") << call; + WEB3_LOG(TRACE) << LOG_DESC("eth_call") << LOG_KV("call", call) + << LOG_KV("blockTag", blockTag) << LOG_KV("blockNumber", blockNumber); } auto&& tx = call.takeToTransaction(m_nodeService->blockFactory()->transactionFactory()); // TODO: ignore params blockNumber here, use it after historical data is available @@ -546,7 +565,9 @@ task::Task EthEndpoint::getBlockByNumber(const Json::Value& request, Json: { auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); auto const ledger = m_nodeService->ledger(); - auto block = co_await ledger::getBlockData(*ledger, blockNumber, bcos::ledger::FULL_BLOCK); + auto flag = bcos::ledger::HEADER; + flag |= fullTransaction ? bcos::ledger::TRANSACTIONS : bcos::ledger::TRANSACTIONS_HASH; + auto block = co_await ledger::getBlockData(*ledger, blockNumber, flag); combineBlockResponse(result, std::move(block), fullTransaction); } catch (...) diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp index 6228992019..134152005f 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/NetEndpoint.cpp @@ -20,25 +20,47 @@ #include "NetEndpoint.h" +#include +#include #include using namespace bcos; using namespace bcos::rpc; -task::Task NetEndpoint::verison(const Json::Value& request, Json::Value& response) +task::Task NetEndpoint::verison(const Json::Value&, Json::Value& response) { - // TODO: get chain id - Json::Value result = "0x4ee8"; // 20200 + auto const ledger = m_nodeService->ledger(); + auto config = co_await ledger::getSystemConfig(*ledger, ledger::SYSTEM_KEY_WEB3_CHAIN_ID); + Json::Value result; + if (config.has_value()) + { + try + { + auto [chainId, _] = config.value(); + result = toQuantity(std::stoull(chainId)); + } + catch (...) + { + result = "0x4ee8"; // 20200 + } + } + else + { + result = "0x4ee8"; // 20200 + } buildJsonContent(result, response); co_return; } -task::Task NetEndpoint::listening(const Json::Value& request, Json::Value& response) +task::Task NetEndpoint::listening(const Json::Value&, Json::Value& response) { Json::Value result = true; buildJsonContent(result, response); co_return; } -task::Task NetEndpoint::peerCount(const Json::Value&, Json::Value&) +task::Task NetEndpoint::peerCount(const Json::Value&, Json::Value& response) { - // TODO: get gateway peer + auto const sync = m_nodeService->sync(); + auto const status = sync->getPeerStatus(); + Json::Value result = Json::UInt64(status.size()); + buildJsonContent(result, response); co_return; } diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.h index 82e4ad0fcf..f0b5a5476e 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/BlockResponse.h @@ -56,7 +56,7 @@ namespace bcos::rpc Json::Value txList = Json::arrayValue; for (size_t i = 0; i < block->transactionsSize(); i++) { - Json::Value txJson; + Json::Value txJson = Json::objectValue; auto tx = block->transaction(i); combineTxResponse(txJson, std::move(tx), nullptr, block); txList.append(txJson); diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h index 0266ff16fe..da0d5a66a1 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h @@ -47,11 +47,24 @@ static void combineTxResponse(Json::Value& result, bcos::protocol::Transaction:: { blockHash = block->blockHeader()->hash(); blockNumber = block->blockHeader()->number(); - for (; transactionIndex < block->transactionsHashSize(); transactionIndex++) + if (block->transactionsSize() == 0) { - if (block->transactionHash(transactionIndex) == tx->hash()) + for (; transactionIndex < block->transactionsHashSize(); transactionIndex++) { - break; + if (block->transactionHash(transactionIndex) == tx->hash()) + { + break; + } + } + } + else + { + for (; transactionIndex < block->transactionsSize(); transactionIndex++) + { + if (block->transaction(transactionIndex)->hash() == tx->hash()) + { + break; + } } } } diff --git a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp index 4a8e9f44c4..fc6ad50b52 100644 --- a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp +++ b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp @@ -142,14 +142,14 @@ BOOST_AUTO_TEST_CASE(handleInvalidTest) BOOST_AUTO_TEST_CASE(handleValidTest) { // method eth_syncing - { - const auto request = - R"({"jsonrpc":"2.0","id":1132123, "method":"eth_syncing","params":[]})"; - auto response = onRPCRequestWrapper(request); - validRespCheck(response); - BOOST_CHECK(response["id"].asInt64() == 1132123); - BOOST_CHECK(response["result"].asBool() == false); - } + // { + // const auto request = + // R"({"jsonrpc":"2.0","id":1132123, "method":"eth_syncing","params":[]})"; + // auto response = onRPCRequestWrapper(request); + // validRespCheck(response); + // BOOST_CHECK(response["id"].asInt64() == 1132123); + // BOOST_CHECK(response["result"].asBool() == false); + // } // method eth_chainId { @@ -180,7 +180,7 @@ BOOST_AUTO_TEST_CASE(handleValidTest) // method eth_gasPrice { - m_ledger->setSystemConfig(SYSTEM_KEY_TX_GAS_PRICE, "10086000"); + m_ledger->setSystemConfig(SYSTEM_KEY_TX_GAS_PRICE, "0x99e670"); const auto request = R"({"jsonrpc":"2.0","id":541321, "method":"eth_gasPrice","params":[]})"; auto response = onRPCRequestWrapper(request); diff --git a/bcos-sync/bcos-sync/BlockSync.cpp b/bcos-sync/bcos-sync/BlockSync.cpp index 2650c3c7bf..d115e589d1 100644 --- a/bcos-sync/bcos-sync/BlockSync.cpp +++ b/bcos-sync/bcos-sync/BlockSync.cpp @@ -287,7 +287,7 @@ bool BlockSync::shouldSyncing() return true; } -bool BlockSync::isSyncing() +bool BlockSync::isSyncing() const { return (m_state == SyncState::Downloading); } @@ -998,3 +998,14 @@ void BlockSync::asyncGetSyncInfo(std::function _o std::string statusStr = fastWriter.write(syncInfo); _onGetSyncInfo(nullptr, statusStr); } + +std::vector BlockSync::getPeerStatus() +{ + std::vector statuses{}; + statuses.reserve(m_syncStatus->peersSize()); + m_syncStatus->foreachPeer([&statuses](auto&& status) { + statuses.emplace_back(status); + return true; + }); + return statuses; +} diff --git a/bcos-sync/bcos-sync/BlockSync.h b/bcos-sync/bcos-sync/BlockSync.h index b652ca3d65..545de9004a 100644 --- a/bcos-sync/bcos-sync/BlockSync.h +++ b/bcos-sync/bcos-sync/BlockSync.h @@ -54,6 +54,8 @@ class BlockSync : public BlockSyncInterface, // for rpc void asyncGetSyncInfo(std::function _onGetSyncInfo) override; + std::vector getPeerStatus() override; + // consensus notify sync module committed block number void asyncNotifyCommittedIndex( bcos::protocol::BlockNumber _number, std::function _onRecv) override @@ -87,6 +89,8 @@ class BlockSync : public BlockSyncInterface, c_FaultyNodeBlockDelta = _delta; } + bool isSyncing() const override; + protected: virtual void asyncNotifyBlockSyncMessage(Error::Ptr _error, bcos::crypto::NodeIDPtr _nodeID, bytesConstRef _data, std::function _sendResponse, @@ -105,7 +109,6 @@ class BlockSync : public BlockSyncInterface, bcos::crypto::NodeIDPtr _nodeID, BlockSyncMsgInterface::Ptr _syncMsg); virtual bool shouldSyncing(); - virtual bool isSyncing(); virtual void tryToRequestBlocks(); virtual void onDownloadTimeout(); // block execute and submit diff --git a/bcos-tars-protocol/bcos-tars-protocol/client/PBFTServiceClient.cpp b/bcos-tars-protocol/bcos-tars-protocol/client/PBFTServiceClient.cpp index 12997a25f6..e227c85bbe 100644 --- a/bcos-tars-protocol/bcos-tars-protocol/client/PBFTServiceClient.cpp +++ b/bcos-tars-protocol/bcos-tars-protocol/client/PBFTServiceClient.cpp @@ -143,6 +143,11 @@ void BlockSyncServiceClient::asyncGetSyncInfo( m_proxy->async_asyncGetSyncInfo(new Callback(_onGetSyncInfo)); } +std::vector BlockSyncServiceClient::getPeerStatus() +{ + return {}; +} + void BlockSyncServiceClient::notifyConnectedNodes(bcos::crypto::NodeIDSet const& _connectedNodes, std::function _onRecvResponse) { diff --git a/bcos-tars-protocol/bcos-tars-protocol/client/PBFTServiceClient.h b/bcos-tars-protocol/bcos-tars-protocol/client/PBFTServiceClient.h index d05574d4f9..f32687020c 100644 --- a/bcos-tars-protocol/bcos-tars-protocol/client/PBFTServiceClient.h +++ b/bcos-tars-protocol/bcos-tars-protocol/client/PBFTServiceClient.h @@ -207,6 +207,8 @@ class BlockSyncServiceClient : virtual public bcos::sync::BlockSyncInterface, void asyncGetSyncInfo( std::function _onGetSyncInfo) override; + std::vector getPeerStatus() override; + // called by the frontService to dispatch message void asyncNotifyBlockSyncMessage(bcos::Error::Ptr _error, std::string const& _uuid, bcos::crypto::NodeIDPtr _nodeID, bcos::bytesConstRef _data, diff --git a/libinitializer/PBFTInitializer.cpp b/libinitializer/PBFTInitializer.cpp index cac523a78f..b8eae331e6 100644 --- a/libinitializer/PBFTInitializer.cpp +++ b/libinitializer/PBFTInitializer.cpp @@ -177,6 +177,9 @@ void PBFTInitializer::initChainNodeInfo( m_nodeInfo->setFeatureKeys( ledger::Features::featureKeys() | RANGES::views::transform([](std::string_view view) { return std::string(view); })); + m_nodeInfo->setSupportConfigs( + ledger::SystemConfigs::supportConfigs() | + RANGES::views::transform([](std::string_view view) { return std::string(view); })); m_groupInfo->appendNodeInfo(m_nodeInfo); INITIALIZER_LOG(INFO) << LOG_DESC("PBFTInitializer::initChainNodeInfo") << LOG_KV("nodeType", m_nodeInfo->nodeType()) From 97d8e5c8b8877b162ce31a33a6f0079cb49a17dd Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Thu, 23 May 2024 17:56:37 +0800 Subject: [PATCH 28/39] (rpc,txpool): fix eth_chain, estimateGas, filter apis bug, fix txpool sync bug. (#4479) --- .../src/executor/TransactionExecutor.cpp | 4 +-- .../precompiled/SystemConfigPrecompiled.cpp | 13 +++++++- bcos-rpc/bcos-rpc/filter/FilterSystem.cpp | 5 ++- bcos-rpc/bcos-rpc/filter/FilterSystem.h | 32 +++++++++---------- .../web3jsonrpc/endpoints/EthEndpoint.cpp | 28 ++++++++++++---- .../web3jsonrpc/model/CallRequest.cpp | 4 +-- .../web3jsonrpc/model/TransactionResponse.h | 8 +++-- bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp | 2 +- .../txpool/storage/MemoryStorage.cpp | 4 +-- .../bcos-utilities/DataConvertUtility.h | 5 +++ 10 files changed, 68 insertions(+), 37 deletions(-) diff --git a/bcos-executor/src/executor/TransactionExecutor.cpp b/bcos-executor/src/executor/TransactionExecutor.cpp index 12112f2824..b54682ede3 100644 --- a/bcos-executor/src/executor/TransactionExecutor.cpp +++ b/bcos-executor/src/executor/TransactionExecutor.cpp @@ -804,8 +804,8 @@ void TransactionExecutor::call(bcos::protocol::ExecutionMessage::UniquePtr input } // Create a temp storage - auto storage = createStateStorage(std::move(prev), true, - m_blockContext->features().get(ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); + auto storage = + createStateStorage(std::move(prev), true, false /*call storage no need set flag*/); // Create a temp block context blockContext = createBlockContextForCall( diff --git a/bcos-executor/src/precompiled/SystemConfigPrecompiled.cpp b/bcos-executor/src/precompiled/SystemConfigPrecompiled.cpp index d32399bf40..93e7ac18f6 100644 --- a/bcos-executor/src/precompiled/SystemConfigPrecompiled.cpp +++ b/bcos-executor/src/precompiled/SystemConfigPrecompiled.cpp @@ -149,12 +149,23 @@ SystemConfigPrecompiled::SystemConfigPrecompiled(crypto::Hash::Ptr hashImpl) : P BOOST_THROW_EXCEPTION( PrecompiledError(fmt::format("unsupported key {}", SYSTEM_KEY_WEB3_CHAIN_ID))); } - if (!isNumStr(_value)) + uint64_t number = 0; + try + { + number = std::stoull(_value); + } + catch (...) { BOOST_THROW_EXCEPTION(PrecompiledError( fmt::format("Invalid value {}, the value for {} must be a number string.", _value, SYSTEM_KEY_WEB3_CHAIN_ID))); } + if (number > UINT32_MAX) + { + BOOST_THROW_EXCEPTION(PrecompiledError( + fmt::format("Invalid value {}, the value for {} must be less than UINT32_MAX.", + _value, SYSTEM_KEY_WEB3_CHAIN_ID))); + } return 0; })); } diff --git a/bcos-rpc/bcos-rpc/filter/FilterSystem.cpp b/bcos-rpc/bcos-rpc/filter/FilterSystem.cpp index 71277c6c67..e85ee49582 100644 --- a/bcos-rpc/bcos-rpc/filter/FilterSystem.cpp +++ b/bcos-rpc/bcos-rpc/filter/FilterSystem.cpp @@ -131,8 +131,7 @@ task::Task FilterSystem::newFilter(std::string_view groupId, Filter co_return toQuantity(id); } -task::Task FilterSystem::getFilterChangeImpl( - std::string_view groupId, uint64_t filterID) +task::Task FilterSystem::getFilterChangeImpl(std::string_view groupId, u256 filterID) { auto filter = getFilterByID(groupId, filterID); if (filter == nullptr) @@ -289,7 +288,7 @@ task::Task FilterSystem::getLogChangeImpl(std::string_view groupId, co_return co_await getLogsInternal(*ledger, std::move(params)); } -task::Task FilterSystem::getFilterLogsImpl(std::string_view groupId, uint64_t filterID) +task::Task FilterSystem::getFilterLogsImpl(std::string_view groupId, u256 filterID) { auto filter = getFilterByID(groupId, filterID); if (filter == nullptr || filter->type() != LogsSubscription) diff --git a/bcos-rpc/bcos-rpc/filter/FilterSystem.h b/bcos-rpc/bcos-rpc/filter/FilterSystem.h index 98d71f2af6..bd6918dd61 100644 --- a/bcos-rpc/bcos-rpc/filter/FilterSystem.h +++ b/bcos-rpc/bcos-rpc/filter/FilterSystem.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -22,9 +23,9 @@ namespace bcos::rpc::filter struct KeyType { std::string group; - uint64_t id; + u256 id; - KeyType(std::string_view g, uint64_t i) : group(g), id(i) {} + KeyType(std::string_view g, u256 i) : group(g), id(i) {} friend bool operator==(const KeyType& l, const KeyType& r) { @@ -48,7 +49,7 @@ struct std::hash size_t operator()(const bcos::rpc::filter::KeyType& key) const noexcept { size_t seed = std::hash{}(key.group); - boost::hash_combine(seed, std::hash{}(key.id)); + boost::hash_combine(seed, std::hash{}(key.id)); return seed; } }; @@ -64,25 +65,22 @@ class FilterSystem : public std::enable_shared_from_this FilterSystem(GroupManager::Ptr groupManager, const std::string& groupId, FilterRequestFactory::Ptr factory, int filterTimeout, int maxBlockProcessPerReq); - virtual ~FilterSystem() - { - m_cleanUpTimer->stop(); - } + virtual ~FilterSystem() { m_cleanUpTimer->stop(); } public: // jsonrpc task::Task newBlockFilter(std::string_view groupId); task::Task newPendingTxFilter(std::string_view groupId); task::Task newFilter(std::string_view groupId, FilterRequest::Ptr params); - task::Task uninstallFilter(std::string_view groupId, uint64_t filterID) + task::Task uninstallFilter(std::string_view groupId, u256 filterID) { co_return uninstallFilterImpl(groupId, filterID); } - task::Task getFilterChanges(std::string_view groupId, uint64_t filterID) + task::Task getFilterChanges(std::string_view groupId, u256 filterID) { co_return co_await getFilterChangeImpl(groupId, filterID); } - task::Task getFilterLogs(std::string_view groupId, uint64_t filterID) + task::Task getFilterLogs(std::string_view groupId, u256 filterID) { co_return co_await getFilterLogsImpl(groupId, filterID); } @@ -98,15 +96,15 @@ class FilterSystem : public std::enable_shared_from_this { co_return co_await newFilter(m_group, params); } - task::Task uninstallFilter(uint64_t filterID) + task::Task uninstallFilter(u256 filterID) { co_return co_await uninstallFilter(m_group, filterID); } - task::Task getFilterChanges(uint64_t filterID) + task::Task getFilterChanges(u256 filterID) { co_return co_await getFilterChanges(m_group, filterID); } - task::Task getFilterLogs(uint64_t filterID) + task::Task getFilterLogs(u256 filterID) { co_return co_await getFilterLogs(m_group, filterID); } @@ -128,15 +126,15 @@ class FilterSystem : public std::enable_shared_from_this NodeService::Ptr getNodeService(std::string_view _groupID, std::string_view _command) const; protected: - bool uninstallFilterImpl(std::string_view groupId, uint64_t filterID) + bool uninstallFilterImpl(std::string_view groupId, u256 filterID) { return m_filters.remove(filter::KeyType(groupId, filterID)) != nullptr; } - task::Task getFilterChangeImpl(std::string_view groupId, uint64_t filterID); + task::Task getFilterChangeImpl(std::string_view groupId, u256 filterID); task::Task getBlockChangeImpl(std::string_view groupId, Filter::Ptr filter); task::Task getPendingTxChangeImpl(std::string_view groupId, Filter::Ptr filter); task::Task getLogChangeImpl(std::string_view groupId, Filter::Ptr filter); - task::Task getFilterLogsImpl(std::string_view groupId, uint64_t filterID); + task::Task getFilterLogsImpl(std::string_view groupId, u256 filterID); task::Task getLogsImpl( std::string_view groupId, FilterRequest::Ptr params, bool needCheckRange); task::Task getLogsInternal( @@ -147,7 +145,7 @@ class FilterSystem : public std::enable_shared_from_this uint64_t insertFilter(Filter::Ptr filter); void cleanUpExpiredFilters(); - Filter::Ptr getFilterByID(std::string_view groupId, uint64_t id) + Filter::Ptr getFilterByID(std::string_view groupId, u256 id) { FilterMap::ReadAccessor::Ptr accessor; if (!m_filters.find(accessor, filter::KeyType(groupId, id))) diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp index 42927268ad..a07f131bcc 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp @@ -74,12 +74,12 @@ task::Task EthEndpoint::chainId(const Json::Value&, Json::Value& response) } catch (...) { - result = "0x4ee8"; // 20200 + result = "0x0"; // 0x0 for default } } else { - result = "0x4ee8"; // 20200 + result = "0x0"; // 0 for default } buildJsonContent(result, response); co_return; @@ -223,8 +223,13 @@ task::Task EthEndpoint::getTransactionCount(const Json::Value& request, Js std::string addressStr(address); boost::algorithm::to_lower(addressStr); // TODO)): blockNumber is ignored nowadays - // auto const blockTag = toView(request[1u]); - // auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); + auto const blockTag = toView(request[1u]); + auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); + if (c_fileLogLevel == TRACE) + { + WEB3_LOG(TRACE) << "eth_getTransactionCount" << LOG_KV("address", address) + << LOG_KV("blockTag", blockTag) << LOG_KV("blockNumber", blockNumber); + } auto const ledger = m_nodeService->ledger(); uint64_t nonce = 0; if (auto const entry = co_await ledger::getStorageAt( @@ -526,6 +531,15 @@ task::Task EthEndpoint::estimateGas(const Json::Value& request, Json::Valu { // params: transaction(TX), blockNumber(QTY|TAG) // result: gas(QTY) + auto const tx = request[0u]; + auto const blockTag = toView(request[1u]); + auto [blockNumber, _] = co_await getBlockNumberByTag(blockTag); + if (c_fileLogLevel == TRACE) + { + WEB3_LOG(TRACE) << LOG_DESC("eth_estimateGas") << LOG_KV("tx", printJson(tx)) + << LOG_KV("blockTag", blockTag) << LOG_KV("blockNumber", blockNumber); + } + // FIXME)): fake now Json::Value result = "0x1"; buildJsonContent(result, response); co_return; @@ -743,7 +757,7 @@ task::Task EthEndpoint::uninstallFilter(const Json::Value& request, Json:: { // params: filterId(QTY) // result: success(Boolean) - auto const id = fromQuantity(std::string(toView(request[0U]))); + auto const id = fromBigQuantity(toView(request[0U])); Json::Value result = co_await m_filterSystem->uninstallFilter(id); buildJsonContent(result, response); co_return; @@ -752,7 +766,7 @@ task::Task EthEndpoint::getFilterChanges(const Json::Value& request, Json: { // params: filterId(QTY) // result: logs(ARRAY) - auto const id = fromQuantity(std::string(toView(request[0U]))); + auto const id = fromBigQuantity(toView(request[0U])); Json::Value result = co_await m_filterSystem->getFilterChanges(id); buildJsonContent(result, response); co_return; @@ -761,7 +775,7 @@ task::Task EthEndpoint::getFilterLogs(const Json::Value& request, Json::Va { // params: filterId(QTY) // result: logs(ARRAY) - auto const id = fromQuantity(std::string(toView(request[0U]))); + auto const id = fromBigQuantity(toView(request[0U])); Json::Value result = co_await m_filterSystem->getFilterLogs(id); buildJsonContent(result, response); co_return; diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/CallRequest.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/model/CallRequest.cpp index 5626624b86..9b4db35f7b 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/CallRequest.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/CallRequest.cpp @@ -45,11 +45,11 @@ std::tuple rpc::decodeCallRequest(Json::Value const& _root) n { return {false, _request}; } - if (!_root.isMember("to") || !_root.isMember("data")) + if (!_root.isMember("data")) { return {false, _request}; } - _request.to = _root["to"].asString(); + _request.to = _root.isMember("to") ? _root["to"].asString() : ""; _request.data = bcos::fromHexWithPrefix(_root["data"].asString()); if (_root.isMember("from")) { diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h index da0d5a66a1..2f4f0ad3f6 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h @@ -86,9 +86,13 @@ static void combineTxResponse(Json::Value& result, bcos::protocol::Transaction:: result["to"] = "0x" + std::move(to); } result["gas"] = toQuantity(tx->gasLimit()); - auto const gasPrice = receipt ? receipt->effectiveGasPrice() : tx->gasPrice(); + auto gasPrice = tx->gasPrice(); + if (receipt && !receipt->effectiveGasPrice().empty()) + { + gasPrice = receipt->effectiveGasPrice(); + } // FIXME)): return will case coredump in executor - result["gasPrice"] = std::string(gasPrice.empty() ? "20200" /*21000*/ : gasPrice); + result["gasPrice"] = std::string(gasPrice.empty() ? "0x0" : gasPrice); result["hash"] = tx->hash().hexPrefixed(); result["input"] = toHexStringWithPrefix(tx->input()); diff --git a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp index fc6ad50b52..4bf2034133 100644 --- a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp +++ b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp @@ -157,7 +157,7 @@ BOOST_AUTO_TEST_CASE(handleValidTest) auto response = onRPCRequestWrapper(request); validRespCheck(response); BOOST_CHECK(response["id"].asInt64() == 123); - BOOST_CHECK(fromQuantity(response["result"].asString()) == 20200); + BOOST_CHECK(fromQuantity(response["result"].asString()) == 0); } // method eth_mining diff --git a/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.cpp b/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.cpp index c0a64be5c8..5d8481500d 100644 --- a/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.cpp +++ b/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.cpp @@ -307,8 +307,8 @@ TransactionStatus MemoryStorage::enforceSubmitTransaction(Transaction::Ptr _tx) { TXPOOL_LOG(WARNING) << LOG_DESC("enforce to seal failed for nonce check failed: ") - << tx->hash().hex() << LOG_KV("batchId", tx->batchId()) - << LOG_KV("batchHash", tx->batchHash().abridged()) + // << tx->hash().hex() << LOG_KV("batchId", tx->batchId()) + // << LOG_KV("batchHash", tx->batchHash().abridged()) << LOG_KV("importTxHash", txHash) << LOG_KV("importBatchId", _tx->batchId()) << LOG_KV("importBatchHash", _tx->batchHash().abridged()); return TransactionStatus::NonceCheckFail; diff --git a/bcos-utilities/bcos-utilities/DataConvertUtility.h b/bcos-utilities/bcos-utilities/DataConvertUtility.h index 04f5245e86..13912a55c4 100644 --- a/bcos-utilities/bcos-utilities/DataConvertUtility.h +++ b/bcos-utilities/bcos-utilities/DataConvertUtility.h @@ -132,6 +132,11 @@ inline uint64_t fromQuantity(std::string const& quantity) return std::stoull(quantity, nullptr, 16); } +inline u256 fromBigQuantity(std::string_view quantity) +{ + return u256(quantity); +} + /** * @brief convert the specified bytes data into hex string * From dc2601661990977cd413abdf623656668ee765a8 Mon Sep 17 00:00:00 2001 From: jdkuang <1950618179@qq.com> Date: Fri, 24 May 2024 16:43:25 +0800 Subject: [PATCH 29/39] (rpc): replace the type of filterId from uint64 to u256 (#4482) Co-authored-by: jdkuang --- bcos-rpc/bcos-rpc/filter/Filter.h | 8 ++++---- bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bcos-rpc/bcos-rpc/filter/Filter.h b/bcos-rpc/bcos-rpc/filter/Filter.h index 71c2136099..3671175fe4 100644 --- a/bcos-rpc/bcos-rpc/filter/Filter.h +++ b/bcos-rpc/bcos-rpc/filter/Filter.h @@ -19,7 +19,7 @@ class Filter public: using Ptr = std::shared_ptr; - Filter(SubscriptionType type, uint64_t id, FilterRequest::Ptr params, bool fullTx, + Filter(SubscriptionType type, u256 id, FilterRequest::Ptr params, bool fullTx, bcos::protocol::BlockNumber startBlockNumber, std::string_view group) : m_fullTx(fullTx), m_type(type), @@ -33,7 +33,7 @@ class Filter virtual ~Filter() {} SubscriptionType type() const { return m_type; } - uint64_t id() const { return m_id; } + u256 id() const { return m_id; } int64_t startBlockNumber() const { return m_startBlockNumber.load(); } FilterRequest::Ptr params() const { return m_params; } bool fullTx() const { return m_fullTx; } @@ -42,12 +42,12 @@ class Filter void updateLastAccessTime() { m_lastAccessTime.store(utcTime()); } void setStartBlockNumber(int64_t number) { m_startBlockNumber.store(number); } - void setId(uint64_t id) { m_id = id; } + void setId(u256 id) { m_id = id; } private: bool m_fullTx; SubscriptionType m_type; - uint64_t m_id; + u256 m_id; FilterRequest::Ptr m_params; std::atomic m_startBlockNumber; std::atomic m_lastAccessTime; diff --git a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp index 6bf392bd19..0c29b71257 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp +++ b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp @@ -1466,31 +1466,31 @@ void JsonRpcImpl_2_0::newFilter( void JsonRpcImpl_2_0::uninstallFilter( std::string_view _groupID, std::string_view filterID, RespFunc _respFunc) { - task::wait([](JsonRpcImpl_2_0* self, std::string_view groupID, uint64_t id, + task::wait([](JsonRpcImpl_2_0* self, std::string_view groupID, u256 id, RespFunc respFunc) -> task::Task { Json::Value jRes = co_await self->filterSystem().uninstallFilter(groupID, id); respFunc(nullptr, jRes); - }(this, _groupID, fromQuantity(std::string(filterID)), std::move(_respFunc))); + }(this, _groupID, fromBigQuantity(filterID), std::move(_respFunc))); } void JsonRpcImpl_2_0::getFilterChanges( std::string_view _groupID, std::string_view filterID, RespFunc _respFunc) { - task::wait([](JsonRpcImpl_2_0* self, std::string_view groupID, uint64_t id, + task::wait([](JsonRpcImpl_2_0* self, std::string_view groupID, u256 id, RespFunc respFunc) -> task::Task { Json::Value jRes = co_await self->filterSystem().getFilterChanges(groupID, id); respFunc(nullptr, jRes); - }(this, _groupID, fromQuantity(std::string(filterID)), std::move(_respFunc))); + }(this, _groupID, fromBigQuantity(filterID), std::move(_respFunc))); } void JsonRpcImpl_2_0::getFilterLogs( std::string_view _groupID, std::string_view filterID, RespFunc _respFunc) { - task::wait([](JsonRpcImpl_2_0* self, std::string_view groupID, uint64_t id, + task::wait([](JsonRpcImpl_2_0* self, std::string_view groupID, u256 id, RespFunc respFunc) -> task::Task { Json::Value jRes = co_await self->filterSystem().getFilterLogs(groupID, id); respFunc(nullptr, jRes); - }(this, _groupID, fromQuantity(std::string(filterID)), std::move(_respFunc))); + }(this, _groupID, fromBigQuantity(filterID), std::move(_respFunc))); } void JsonRpcImpl_2_0::getLogs( From 3c6fdab2d26a08beeeee60e871a801c9e21f90a9 Mon Sep 17 00:00:00 2001 From: MO NAN <651932351@qq.com> Date: Mon, 27 May 2024 10:57:31 +0800 Subject: [PATCH 30/39] sync code to fix ubuntu ci problem (#4483) Co-authored-by: Kyon <32325790+kyonRay@users.noreply.github.com> --- .../workflows/workflow-self-hosted-mac.yml | 4 + README.md | 4 +- .../bcos-framework/storage2/MemoryStorage.h | 49 +- .../bcos-framework/storage2/Storage.h | 4 + .../unittests/storage2/TestMemoryStorage.cpp | 54 +- bcos-storage/bcos-storage/RocksDBStorage2.h | 14 +- .../test/unittest/TestRocksDBStorage2.cpp | 2 +- .../bcos-utilities/RecursiveLambda.h | 14 + .../BaselineSchedulerInitializer.cpp | 26 +- .../vm/EVMHostInterface.h | 1 + .../vm/HostContext.h | 85 ++- .../BaselineScheduler.h | 24 +- .../MultiLayerStorage.h | 683 ++++++++---------- .../SchedulerParallelImpl.h | 235 +++--- .../benchmark/benchmarkMultiLayerStorage.cpp | 19 +- .../benchmark/benchmarkScheduler.cpp | 41 +- .../tests/testMultiLayerStorage.cpp | 73 +- .../tests/testSchedulerParallel.cpp | 21 +- .../tests/testSchedulerSerial.cpp | 3 +- 19 files changed, 697 insertions(+), 659 deletions(-) create mode 100644 bcos-utilities/bcos-utilities/RecursiveLambda.h diff --git a/.github/workflows/workflow-self-hosted-mac.yml b/.github/workflows/workflow-self-hosted-mac.yml index 9e2f316c8f..7fe7376f18 100644 --- a/.github/workflows/workflow-self-hosted-mac.yml +++ b/.github/workflows/workflow-self-hosted-mac.yml @@ -5,6 +5,10 @@ on: - "docs/**" - "Changelog.md" - "README.md" + branches: + - release-3.* + - feature-* + - master release: types: [push] concurrency: diff --git a/README.md b/README.md index 0dabb8db1a..bbfc88f7fd 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ FISCO BCOS(读作/ˈfɪskl bi:ˈkɒz/) 是一个稳定、高效、安全的 单链配置下,性能TPS可达10万+。全面支持国密算法、国产操作系统与国产CPU架构。包含区块流水线、可拔插共识机制、全方位并行计算、区块链文件系统、权限治理框架、分布式存储等特性。 ## 版本信息 -- 稳定版本(生产环境使用):v3.2.3,版本内容可参考[《FISCO-BCOS v3.2.3版本说明》](https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v3.2.3) -- 最新版本(用户体验新特性):v3.7.1,版本内容可参考 [《FISCO-BCOS v3.7.1版本说明》](https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v3.7.1) +- 稳定版本(生产环境使用):v3.2.7,版本内容可参考[《FISCO-BCOS v3.2.7版本说明》](https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v3.2.7) +- 最新版本(用户体验新特性):v3.8.0,版本内容可参考 [《FISCO-BCOS v3.8.0版本说明》](https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v3.8.0) ## 系统概述 FISCO BCOS系统架构包括基础层、核心层、服务层、用户层和接入层提供稳定、安全的区块链底层服务。中间件层通过可视化界面,简化了用户管理区块链系统的流程。右侧配套相关开发、运维、安全控制的组件,辅助应用落地过程中不同角色的需要;同时,提供隐私保护和跨链相关的技术组件,满足不同场景的应用诉求。 diff --git a/bcos-framework/bcos-framework/storage2/MemoryStorage.h b/bcos-framework/bcos-framework/storage2/MemoryStorage.h index 8f93db66fd..0515eae50a 100644 --- a/bcos-framework/bcos-framework/storage2/MemoryStorage.h +++ b/bcos-framework/bcos-framework/storage2/MemoryStorage.h @@ -404,21 +404,26 @@ class MemoryStorage } } - template class Iterator { private: - Begin m_begin; - End m_end; + std::reference_wrapper m_buckets; + size_t m_bucketIndex = 0; + RANGES::iterator_t m_begin; + RANGES::iterator_t m_end; + + using IteratorValue = + std::conditional_t; public: - Iterator(Begin begin, End end) : m_begin(begin), m_end(end) {} + Iterator(const Buckets& buckets) + : m_buckets(buckets), + m_begin((m_buckets.get()[m_bucketIndex]).container.begin()), + m_end((m_buckets.get()[m_bucketIndex]).container.end()) + {} auto next() { - using IteratorValue = - std::conditional_t; - std::optional> result; if (m_begin != m_end) { @@ -433,18 +438,40 @@ class MemoryStorage result.emplace(std::make_tuple(std::cref(data.key), std::cref(data.value))); } ++m_begin; + return task::AwaitableValue(std::move(result)); + } + + if (m_bucketIndex + 1 < m_buckets.get().size()) + { + ++m_bucketIndex; + m_begin = m_buckets.get()[m_bucketIndex].container.begin(); + m_end = m_buckets.get()[m_bucketIndex].container.end(); + return next(); } return task::AwaitableValue(std::move(result)); } + + auto seek(auto&& key) + requires(!withConcurrent && withOrdered) + { + auto const& index = m_buckets.get()[m_bucketIndex].container.template get<0>(); + m_begin = index.lower_bound(std::forward(key)); + } }; friend auto tag_invoke( bcos::storage2::tag_t /*unused*/, MemoryStorage const& storage) - requires(!withConcurrent) { - return task::AwaitableValue(Iterator( - storage.m_buckets[0].container.begin(), storage.m_buckets[0].container.end())); + return task::AwaitableValue(Iterator(storage.m_buckets)); + } + + friend auto tag_invoke(bcos::storage2::tag_t /*unused*/, + MemoryStorage const& storage, RANGE_SEEK_TYPE /*unused*/, auto&& key) + requires(!withConcurrent && withOrdered) + { + auto iterator = Iterator(storage.m_buckets); + iterator.seek(std::forward(key)); + return task::AwaitableValue(std::move(iterator)); } bool empty() const diff --git a/bcos-framework/bcos-framework/storage2/Storage.h b/bcos-framework/bcos-framework/storage2/Storage.h index 1b9e087f44..39c9bdb130 100644 --- a/bcos-framework/bcos-framework/storage2/Storage.h +++ b/bcos-framework/bcos-framework/storage2/Storage.h @@ -13,6 +13,10 @@ inline constexpr struct DIRECT_TYPE { } DIRECT{}; +inline constexpr struct RANGE_SEEK_TYPE +{ +} RANGE_SEEK{}; + template using ReturnType = typename task::AwaitableReturnType; template diff --git a/bcos-framework/test/unittests/storage2/TestMemoryStorage.cpp b/bcos-framework/test/unittests/storage2/TestMemoryStorage.cpp index c59ec40dec..0ee8af1bf0 100644 --- a/bcos-framework/test/unittests/storage2/TestMemoryStorage.cpp +++ b/bcos-framework/test/unittests/storage2/TestMemoryStorage.cpp @@ -253,15 +253,27 @@ BOOST_AUTO_TEST_CASE(range) { BOOST_CHECK(kv); auto& [key, value] = *kv; - auto& [tableName, keyName] = key; - // BOOST_CHECK_EQUAL(tableName, "table"); - // BOOST_CHECK_EQUAL(keyName, "key:" + boost::lexical_cast(num)); - // BOOST_CHECK_EQUAL(value->get(), "Hello world!" + - // boost::lexical_cast(num)); + const auto& [tableName, keyName] = key; BOOST_CHECK_LT(num, 100); ++num; } BOOST_CHECK_EQUAL(num, 100); + + MemoryStorage + intStorage; + co_await storage2::writeSome( + intStorage, RANGES::iota_view(0, 10), RANGES::repeat_view(100)); + auto start = 4; + auto range3 = co_await storage2::range(intStorage, storage2::RANGE_SEEK, start); + while (auto pair = co_await range3.next()) + { + auto&& [key, value] = *pair; + BOOST_CHECK_EQUAL(key, start++); + } + BOOST_CHECK_EQUAL(start, 10); }()); } @@ -341,4 +353,36 @@ BOOST_AUTO_TEST_CASE(keyComp) BOOST_CHECK_EQUAL(hash1, hash2); } +BOOST_AUTO_TEST_CASE(concurrentRange) +{ + task::syncWait([]() -> task::Task { + constexpr static int count = 100; + + MemoryStorage> + storage; + + co_await storage2::writeSome(storage, + RANGES::iota_view(0, count) | RANGES::views::transform([](auto num) { + return boost::lexical_cast(num); + }), + RANGES::iota_view(0, count) | RANGES::views::transform([](auto num) { + storage::Entry entry; + entry.set("Hello world!" + boost::lexical_cast(num)); + return entry; + })); + + auto range = co_await storage2::range(storage); + auto expect = count; + while (auto value = co_await range.next()) + { + --expect; + auto&& [key, entry] = *value; + auto index = boost::lexical_cast(key); + BOOST_CHECK_LT(index, count); + } + BOOST_CHECK_EQUAL(expect, 0); + }()); +} + BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/bcos-storage/bcos-storage/RocksDBStorage2.h b/bcos-storage/bcos-storage/RocksDBStorage2.h index 14e0fce6d7..806d5d3406 100644 --- a/bcos-storage/bcos-storage/RocksDBStorage2.h +++ b/bcos-storage/bcos-storage/RocksDBStorage2.h @@ -28,13 +28,9 @@ namespace bcos::storage2::rocksdb template concept Resolver = requires(ResolverType&& resolver) { - { - resolver.encode(std::declval()) - }; - { - resolver.decode(std::string_view{}) - } -> std::convertible_to; - }; + { resolver.encode(std::declval()) }; + { resolver.decode(std::string_view{}) } -> std::convertible_to; +}; // clang-format off struct RocksDBException : public bcos::Error {}; @@ -129,7 +125,7 @@ class RocksDBStorage2 friend auto tag_invoke(storage2::tag_t /*unused*/, RocksDBStorage2& storage, RANGES::input_range auto&& keys) -> task::AwaitableValue(keys)))> + std::forward(keys)))> { return {executeReadSome(storage, std::forward(keys))}; } @@ -404,7 +400,7 @@ class RocksDBStorage2 friend task::AwaitableValue tag_invoke( bcos::storage2::tag_t /*unused*/, RocksDBStorage2& storage, - auto&& startKey) + storage2::RANGE_SEEK_TYPE /*unused*/, auto&& startKey) { auto encodedKey = storage.m_keyResolver.encode(startKey); ::rocksdb::Slice slice(RANGES::data(encodedKey), RANGES::size(encodedKey)); diff --git a/bcos-storage/test/unittest/TestRocksDBStorage2.cpp b/bcos-storage/test/unittest/TestRocksDBStorage2.cpp index 609eea2a01..8e6e74e263 100644 --- a/bcos-storage/test/unittest/TestRocksDBStorage2.cpp +++ b/bcos-storage/test/unittest/TestRocksDBStorage2.cpp @@ -190,7 +190,7 @@ BOOST_AUTO_TEST_CASE(merge) BOOST_CHECK_EQUAL(i, 100); StateKeyView seekKeyView{"Table~5"sv, "Key~5"sv}; - auto seekIt2 = co_await storage2::range(rocksDB, seekKeyView); + auto seekIt2 = co_await storage2::range(rocksDB, storage2::RANGE_SEEK, seekKeyView); int j = 54; while (auto keyValue = co_await seekIt2.next()) diff --git a/bcos-utilities/bcos-utilities/RecursiveLambda.h b/bcos-utilities/bcos-utilities/RecursiveLambda.h new file mode 100644 index 0000000000..8644725796 --- /dev/null +++ b/bcos-utilities/bcos-utilities/RecursiveLambda.h @@ -0,0 +1,14 @@ +#pragma once +#include + +namespace bcos +{ + +inline constexpr auto recursiveLambda(auto&& lambda) +{ + return [lambdaImpl = std::forward(lambda)](auto&&... args) -> decltype(auto) { + return lambdaImpl(lambdaImpl, std::forward(args)...); + }; +}; + +} // namespace bcos \ No newline at end of file diff --git a/libinitializer/BaselineSchedulerInitializer.cpp b/libinitializer/BaselineSchedulerInitializer.cpp index d3fb91f613..c6ceb3f4a3 100644 --- a/libinitializer/BaselineSchedulerInitializer.cpp +++ b/libinitializer/BaselineSchedulerInitializer.cpp @@ -49,6 +49,18 @@ bcos::task::Task bcos::transaction_scheduler::BaselineSchedulerInitializer } } +using MutableStorage = bcos::storage2::memory_storage::MemoryStorage< + bcos::transaction_executor::StateKey, bcos::transaction_executor::StateValue, + bcos::storage2::memory_storage::Attribute(bcos::storage2::memory_storage::ORDERED | + bcos::storage2::memory_storage::LOGICAL_DELETION)>; +using CacheStorage = + bcos::storage2::memory_storage::MemoryStorage>; + std::tuple()>, std::function)>> bcos::transaction_scheduler::BaselineSchedulerInitializer::build(::rocksdb::DB& rocksDB, @@ -60,18 +72,6 @@ bcos::transaction_scheduler::BaselineSchedulerInitializer::build(::rocksdb::DB& { struct Data { - using MutableStorage = - storage2::memory_storage::MemoryStorage; - using CacheStorage = storage2::memory_storage::MemoryStorage>; - CacheStorage m_cacheStorage; storage2::rocksdb::RocksDBStorage2()); + return buildBaselineHolder(std::make_shared>()); } return buildBaselineHolder(std::make_shared()); } diff --git a/transaction-executor/bcos-transaction-executor/vm/EVMHostInterface.h b/transaction-executor/bcos-transaction-executor/vm/EVMHostInterface.h index 8f03e11aef..affa99df5a 100644 --- a/transaction-executor/bcos-transaction-executor/vm/EVMHostInterface.h +++ b/transaction-executor/bcos-transaction-executor/vm/EVMHostInterface.h @@ -23,6 +23,7 @@ #include "bcos-concepts/ByteBuffer.h" #include "bcos-executor/src/Common.h" +#include "bcos-framework/protocol/Exceptions.h" #include #include #include diff --git a/transaction-executor/bcos-transaction-executor/vm/HostContext.h b/transaction-executor/bcos-transaction-executor/vm/HostContext.h index a53b56f21a..a680bf5c3d 100644 --- a/transaction-executor/bcos-transaction-executor/vm/HostContext.h +++ b/transaction-executor/bcos-transaction-executor/vm/HostContext.h @@ -121,15 +121,15 @@ template class HostContext : public evmc_host_context { private: - Storage& m_rollbackableStorage; - protocol::BlockHeader const& m_blockHeader; - const evmc_address& m_origin; + std::reference_wrapper m_rollbackableStorage; + std::reference_wrapper m_blockHeader; + std::reference_wrapper m_origin; std::string_view m_abi; int m_contextID; - int64_t& m_seq; - PrecompiledManager const& m_precompiledManager; - ledger::LedgerConfig const& m_ledgerConfig; - crypto::Hash const& m_hashImpl; + std::reference_wrapper m_seq; + std::reference_wrapper m_precompiledManager; + std::reference_wrapper m_ledgerConfig; + std::reference_wrapper m_hashImpl; std::variant m_message; Account m_myAccount; @@ -155,7 +155,7 @@ class HostContext : public evmc_host_context auto getMyAccount() { - return Account>(m_rollbackableStorage, message().recipient); + return Account>(m_rollbackableStorage.get(), message().recipient); } inline constexpr static struct InnerConstructor @@ -182,7 +182,8 @@ class HostContext : public evmc_host_context m_precompiledManager(precompiledManager), m_ledgerConfig(ledgerConfig), m_hashImpl(hashImpl), - m_message(getMessage(message, m_blockHeader.number(), m_contextID, m_seq, m_hashImpl)), + m_message( + getMessage(message, m_blockHeader.get().number(), m_contextID, m_seq, m_hashImpl)), m_myAccount(getMyAccount()) {} @@ -214,7 +215,7 @@ class HostContext : public evmc_host_context task::Task> code(const evmc_address& address) { - if (auto executable = co_await getExecutable(m_rollbackableStorage, address); + if (auto executable = co_await getExecutable(m_rollbackableStorage.get(), address); executable && executable->m_code) { co_return executable->m_code; @@ -224,7 +225,7 @@ class HostContext : public evmc_host_context task::Task codeSizeAt(const evmc_address& address) { - if (auto const* precompiled = m_precompiledManager.getPrecompiled(address)) + if (auto const* precompiled = m_precompiledManager.get().getPrecompiled(address)) { co_return transaction_executor::size(*precompiled); } @@ -238,7 +239,7 @@ class HostContext : public evmc_host_context task::Task codeHashAt(const evmc_address& address) { - Account account(m_rollbackableStorage, address); + Account account(m_rollbackableStorage.get(), address); co_return co_await ledger::account::codeHash(account); } @@ -259,12 +260,15 @@ class HostContext : public evmc_host_context BOOST_THROW_EXCEPTION(std::runtime_error("Unsupported method!")); co_return h256{}; } - int64_t blockNumber() const { return m_blockHeader.number(); } - uint32_t blockVersion() const { return m_blockHeader.version(); } - int64_t timestamp() const { return m_blockHeader.timestamp(); } + int64_t blockNumber() const { return m_blockHeader.get().number(); } + uint32_t blockVersion() const { return m_blockHeader.get().version(); } + int64_t timestamp() const { return m_blockHeader.get().timestamp(); } evmc_address const& origin() const { return m_origin; } - int64_t blockGasLimit() const { return std::get<0>(m_ledgerConfig.gasLimit()); } - evmc_uint256be chainId() const { return m_ledgerConfig.chainId().value_or(evmc_uint256be{}); } + int64_t blockGasLimit() const { return std::get<0>(m_ledgerConfig.get().gasLimit()); } + evmc_uint256be chainId() const + { + return m_ledgerConfig.get().chainId().value_or(evmc_uint256be{}); + } /// Revert any changes made (by any of the other calls). void log(const evmc_address& address, h256s topics, bytesConstRef data) @@ -302,20 +306,20 @@ class HostContext : public evmc_host_context if (c_fileLogLevel <= LogLevel::TRACE) [[unlikely]] { HOST_CONTEXT_LOG(TRACE) - << "HostContext execute, kind: " << ref.kind << " seq:" << m_seq + << "HostContext execute, kind: " << ref.kind << " seq:" << m_seq.get() << " sender:" << address2HexString(ref.sender) << " recipient:" << address2HexString(ref.recipient) << " gas:" << ref.gas; } - auto savepoint = m_rollbackableStorage.current(); + auto savepoint = m_rollbackableStorage.get().current(); std::optional evmResult; - if (m_ledgerConfig.authCheckStatus() != 0U) + if (m_ledgerConfig.get().authCheckStatus() != 0U) { - HOST_CONTEXT_LOG(DEBUG) - << "Checking auth..." << m_ledgerConfig.authCheckStatus() << " gas: " << ref.gas; + HOST_CONTEXT_LOG(DEBUG) << "Checking auth..." << m_ledgerConfig.get().authCheckStatus() + << " gas: " << ref.gas; - if (auto result = checkAuth(m_rollbackableStorage, m_blockHeader, ref, m_origin, - buildLegacyExternalCaller(), m_precompiledManager, m_contextID, m_seq, + if (auto result = checkAuth(m_rollbackableStorage.get(), m_blockHeader, ref, m_origin, + buildLegacyExternalCaller(), m_precompiledManager.get(), m_contextID, m_seq, m_hashImpl)) { HOST_CONTEXT_LOG(DEBUG) << "Auth check failed"; @@ -339,7 +343,7 @@ class HostContext : public evmc_host_context // If the call to system contract failed, the gasUsed is cleared to zero if (evmResult->status_code != EVMC_SUCCESS) { - co_await m_rollbackableStorage.rollback(savepoint); + co_await m_rollbackableStorage.get().rollback(savepoint); if (auto hexAddress = address2FixedArray(ref.code_address); bcos::precompiled::c_systemTxsAddress.find(concepts::bytebuffer::toView( @@ -386,9 +390,9 @@ class HostContext : public evmc_host_context << " recipient:" << address2HexString(message.recipient) << " gas:" << message.gas; } - HostContext hostcontext(innerConstructor, m_rollbackableStorage, m_blockHeader, message, - m_origin, {}, m_contextID, m_seq, m_precompiledManager, m_ledgerConfig, m_hashImpl, - interface); + HostContext hostcontext(innerConstructor, m_rollbackableStorage.get(), m_blockHeader, + message, m_origin, {}, m_contextID, m_seq, m_precompiledManager.get(), m_ledgerConfig, + m_hashImpl, interface); try { @@ -433,11 +437,11 @@ class HostContext : public evmc_host_context task::Task executeCreate() { - if (m_blockHeader.number() != 0) + if (m_blockHeader.get().number() != 0) { - createAuthTable(m_rollbackableStorage, m_blockHeader, message(), m_origin, + createAuthTable(m_rollbackableStorage.get(), m_blockHeader, message(), m_origin, co_await ledger::account::path(m_myAccount), buildLegacyExternalCaller(), - m_precompiledManager, m_contextID, m_seq); + m_precompiledManager.get(), m_contextID, m_seq); } auto& ref = message(); @@ -447,7 +451,7 @@ class HostContext : public evmc_host_context if (result.status_code == 0) { auto code = bytesConstRef(result.output_data, result.output_size); - auto codeHash = m_hashImpl.hash(code); + auto codeHash = m_hashImpl.get().hash(code); co_await ledger::account::setCode( m_myAccount, code.toBytes(), std::string(m_abi), codeHash); @@ -474,10 +478,10 @@ class HostContext : public evmc_host_context if (message().kind != EVMC_DELEGATECALL) { if (auto const* precompiled = - m_precompiledManager.getPrecompiled(message().code_address)) + m_precompiledManager.get().getPrecompiled(message().code_address)) { if (auto flag = transaction_executor::featureFlag(*precompiled); - !flag || m_ledgerConfig.features().get(*flag)) + !flag || m_ledgerConfig.get().features().get(*flag)) { m_preparedPrecompiled = precompiled; co_return; @@ -485,7 +489,7 @@ class HostContext : public evmc_host_context } } - m_executable = co_await getExecutable(m_rollbackableStorage, message().code_address); + m_executable = co_await getExecutable(m_rollbackableStorage.get(), message().code_address); if (m_executable && hasPrecompiledPrefix(m_executable->m_code->data())) { if (std::holds_alternative(m_message)) @@ -524,7 +528,8 @@ class HostContext : public evmc_host_context << LOG_KV("code", code); } - if (auto const* precompiled = m_precompiledManager.getPrecompiled(message.recipient)) + if (auto const* precompiled = + m_precompiledManager.get().getPrecompiled(message.recipient)) { m_preparedPrecompiled = precompiled; } @@ -542,14 +547,16 @@ class HostContext : public evmc_host_context if (m_preparedPrecompiled != nullptr) { co_return transaction_executor::callPrecompiled(*m_preparedPrecompiled, - m_rollbackableStorage, m_blockHeader, ref, m_origin, buildLegacyExternalCaller(), - m_precompiledManager, m_contextID, m_seq, m_ledgerConfig.authCheckStatus()); + m_rollbackableStorage.get(), m_blockHeader, ref, m_origin, + buildLegacyExternalCaller(), m_precompiledManager.get(), m_contextID, m_seq, + m_ledgerConfig.get().authCheckStatus()); } else { if (!m_executable) { - m_executable = co_await getExecutable(m_rollbackableStorage, ref.code_address); + m_executable = + co_await getExecutable(m_rollbackableStorage.get(), ref.code_address); } if (!m_executable) diff --git a/transaction-scheduler/bcos-transaction-scheduler/BaselineScheduler.h b/transaction-scheduler/bcos-transaction-scheduler/BaselineScheduler.h index be0ac2a3af..7df398a7da 100644 --- a/transaction-scheduler/bcos-transaction-scheduler/BaselineScheduler.h +++ b/transaction-scheduler/bcos-transaction-scheduler/BaselineScheduler.h @@ -310,8 +310,8 @@ class BaselineScheduler : public scheduler::SchedulerInterface } auto now = current(); - scheduler.m_multiLayerStorage.get().newMutable(); - auto view = scheduler.m_multiLayerStorage.get().fork(true); + auto view = scheduler.m_multiLayerStorage.get().fork(); + view.newMutable(); auto transactions = co_await getTransactions(scheduler.m_txpool.get(), *block); ledger::LedgerConfig::Ptr ledgerConfig; @@ -329,8 +329,8 @@ class BaselineScheduler : public scheduler::SchedulerInterface auto executedBlockHeader = scheduler.m_blockHeaderFactory.get().populateBlockHeader(blockHeader); bool sysBlock = false; - finishExecute(scheduler.m_multiLayerStorage.get().mutableStorage(), receipts, - *executedBlockHeader, *block, transactions, sysBlock, scheduler.m_hashImpl.get()); + finishExecute(view.mutableStorage(), receipts, *executedBlockHeader, *block, + transactions, sysBlock, scheduler.m_hashImpl.get()); if (verify && (executedBlockHeader->hash() != blockHeader->hash())) { @@ -354,13 +354,12 @@ class BaselineScheduler : public scheduler::SchedulerInterface } BASELINE_SCHEDULER_LOG(ERROR) << message; - scheduler.m_multiLayerStorage.get().removeMutable(); co_return std::make_tuple( BCOS_ERROR_UNIQUE_PTR(scheduler::SchedulerError::InvalidBlocks, message), nullptr, false); } - scheduler.m_multiLayerStorage.get().pushMutableToImmutableFront(); + scheduler.m_multiLayerStorage.get().pushView(std::move(view)); scheduler.m_lastExecutedBlockNumber = blockHeader->number(); std::unique_lock resultsLock(scheduler.m_resultsMutex); @@ -449,7 +448,7 @@ class BaselineScheduler : public scheduler::SchedulerInterface resultsLock.unlock(); result.m_block->setBlockHeader(header); - auto lastStorage = scheduler.m_multiLayerStorage.get().lastImmutableStorage(); + auto lastStorage = scheduler.m_multiLayerStorage.get().backStorage(); if (result.m_block->blockHeaderConst()->number() != 0) { ittapi::Report report(ittapi::ITT_DOMAINS::instance().BASE_SCHEDULER, @@ -458,8 +457,7 @@ class BaselineScheduler : public scheduler::SchedulerInterface co_await ledger::prewriteBlock(scheduler.m_ledger.get(), result.m_transactions, result.m_block, false, *lastStorage); } - auto mergedStorage = - co_await scheduler.m_multiLayerStorage.get().mergeAndPopImmutableBack(); + auto mergedStorage = co_await scheduler.m_multiLayerStorage.get().mergeBackStorage(); co_await ledger::storeTransactionsAndReceipts( scheduler.m_ledger.get(), result.m_transactions, result.m_block); @@ -580,8 +578,8 @@ class BaselineScheduler : public scheduler::SchedulerInterface { task::wait([](decltype(this) self, protocol::Transaction::Ptr transaction, decltype(callback) callback) -> task::Task { - auto view = self->m_multiLayerStorage.get().fork(false); - view.newTemporaryMutable(); + auto view = self->m_multiLayerStorage.get().fork(); + view.newMutable(); auto blockHeader = self->m_blockHeaderFactory.get().createBlockHeader(); ledger::LedgerConfig::Ptr ledgerConfig; { @@ -622,7 +620,7 @@ class BaselineScheduler : public scheduler::SchedulerInterface { task::wait([](decltype(this) self, std::string_view contract, decltype(callback) callback) -> task::Task { - auto view = self->m_multiLayerStorage.get().fork(false); + auto view = self->m_multiLayerStorage.get().fork(); auto contractAddress = unhexAddress(contract); ledger::account::EVMAccount account(view, contractAddress); auto code = co_await ledger::account::code(account); @@ -642,7 +640,7 @@ class BaselineScheduler : public scheduler::SchedulerInterface { task::wait([](decltype(this) self, std::string_view contract, decltype(callback) callback) -> task::Task { - auto view = self->m_multiLayerStorage.get().fork(false); + auto view = self->m_multiLayerStorage.get().fork(); auto contractAddress = unhexAddress(contract); ledger::account::EVMAccount account(view, contractAddress); auto abi = co_await ledger::account::abi(account); diff --git a/transaction-scheduler/bcos-transaction-scheduler/MultiLayerStorage.h b/transaction-scheduler/bcos-transaction-scheduler/MultiLayerStorage.h index 27c9e3ec44..f0a2b8d4cb 100644 --- a/transaction-scheduler/bcos-transaction-scheduler/MultiLayerStorage.h +++ b/transaction-scheduler/bcos-transaction-scheduler/MultiLayerStorage.h @@ -1,8 +1,9 @@ #pragma once #include "bcos-framework/storage2/Storage.h" +#include "bcos-task/TBBWait.h" #include "bcos-task/Trait.h" -#include "bcos-task/Wait.h" #include "bcos-utilities/Error.h" +#include "bcos-utilities/RecursiveLambda.h" #include #include #include @@ -23,275 +24,250 @@ struct UnsupportedMethod : public bcos::Error {}; template requires((std::is_void_v || (!std::is_void_v))) -class MultiLayerStorage +class View { -private: +public: constexpr static bool withCacheStorage = !std::is_void_v; using KeyType = std::remove_cvref_t; using ValueType = std::remove_cvref_t; - static_assert(std::same_as); - static_assert(std::same_as); + using MutableStorage = MutableStorageType; std::shared_ptr m_mutableStorage; std::deque> m_immutableStorages; - std::mutex m_listMutex; - std::mutex m_mergeMutex; - std::reference_wrapper> m_backendStorage; [[no_unique_address]] std::conditional_t>, std::monostate> m_cacheStorage; - // 同一时间只允许一个可以修改的view - // Only one view that can be modified is allowed at a time - std::mutex m_mutableMutex; - -public: - using MutableStorage = MutableStorageType; + View(BackendStorage& backendStorage) + requires(!withCacheStorage) + : m_backendStorage(backendStorage) + {} + View(BackendStorage& backendStorage, + std::conditional_t, + std::monostate> + cacheStorage) + requires(withCacheStorage) + : m_backendStorage(backendStorage), m_cacheStorage(cacheStorage) + {} - class View + static task::Task fillMissingValues( + auto& storage, RANGES::input_range auto&& keys, RANGES::input_range auto& values) { - friend class MultiLayerStorage; - - private: - std::shared_ptr m_mutableStorage; - std::deque> m_immutableStorages; - std::reference_wrapper> m_backendStorage; - [[no_unique_address]] std::conditional_t>, std::monostate> - m_cacheStorage; - std::unique_lock m_mutableLock; - - View(BackendStorage& backendStorage) - requires(!withCacheStorage) - : m_backendStorage(backendStorage) - {} - View(BackendStorage& backendStorage, - std::conditional_t, - std::monostate> - cacheStorage) - requires(withCacheStorage) - : m_backendStorage(backendStorage), m_cacheStorage(cacheStorage) - {} - - static task::Task fillMissingValues( - auto& storage, RANGES::input_range auto&& keys, RANGES::input_range auto& values) - { - using StoreKeyType = std::conditional_t< - std::is_lvalue_reference_v>, + using StoreKeyType = + std::conditional_t>, std::reference_wrapper, KeyType>; - std::vector>>> - missingKeyValues; - for (auto&& [key, value] : - RANGES::views::zip(std::forward(keys), values)) - { - if (!value) - { - missingKeyValues.emplace_back( - std::forward(key), std::ref(value)); - } - } - auto gotValues = - co_await storage2::readSome(storage, missingKeyValues | RANGES::views::keys); - - size_t count = 0; - for (auto&& [from, to] : - RANGES::views::zip(gotValues, missingKeyValues | RANGES::views::values)) + std::vector>>> + missingKeyValues; + for (auto&& [key, value] : RANGES::views::zip(std::forward(keys), values)) + { + if (!value) { - if (from) - { - to.get() = std::move(from); - ++count; - } + missingKeyValues.emplace_back(std::forward(key), std::ref(value)); } - - co_return count == RANGES::size(gotValues); } + auto gotValues = + co_await storage2::readSome(storage, missingKeyValues | RANGES::views::keys); - public: - using MutableStorage = MutableStorageType; - - friend auto tag_invoke(storage2::tag_t /*unused*/, View& storage, - RANGES::input_range auto&& keys) - -> task::Task>> - requires RANGES::sized_range && - RANGES::sized_range>> + size_t count = 0; + for (auto&& [from, to] : + RANGES::views::zip(gotValues, missingKeyValues | RANGES::views::values)) { - task::AwaitableReturnType - values(RANGES::size(keys)); - if (storage.m_mutableStorage && - co_await fillMissingValues(*storage.m_mutableStorage, keys, values)) - { - co_return values; - } - else + if (from) { - values.resize(RANGES::size(keys)); - } - - for (auto& immutableStorage : storage.m_immutableStorages) - { - if (co_await fillMissingValues(*immutableStorage, keys, values)) - { - co_return values; - } + to.get() = std::move(from); + ++count; } + } - if constexpr (withCacheStorage) - { - if (co_await fillMissingValues(storage.m_cacheStorage.get(), keys, values)) - { - co_return values; - } - } + co_return count == RANGES::size(gotValues); + } - co_await fillMissingValues(storage.m_backendStorage.get(), keys, values); + friend auto tag_invoke( + storage2::tag_t /*unused*/, View& view, RANGES::input_range auto&& keys) + -> task::Task>> + requires RANGES::sized_range && + RANGES::sized_range>> + { + task::AwaitableReturnType + values(RANGES::size(keys)); + if (view.m_mutableStorage && + co_await fillMissingValues(*view.m_mutableStorage, keys, values)) + { co_return values; } + else + { + values.resize(RANGES::size(keys)); + } - friend auto tag_invoke(storage2::tag_t /*unused*/, View& storage, - RANGES::input_range auto&& keys, storage2::DIRECT_TYPE /*unused*/) - -> task::Task>> + for (auto& immutableStorage : view.m_immutableStorages) { - if (storage.m_mutableStorage) + if (co_await fillMissingValues(*immutableStorage, keys, values)) { - co_return co_await storage2::readSome( - *storage.m_mutableStorage, std::forward(keys)); + co_return values; } + } - for (auto& immutableStorage : storage.m_immutableStorages) + if constexpr (withCacheStorage) + { + if (co_await fillMissingValues(view.m_cacheStorage.get(), keys, values)) { - co_return co_await storage2::readSome( - *immutableStorage, std::forward(keys)); + co_return values; } + } - if constexpr (withCacheStorage) - { - co_return co_await storage2::readSome( - storage.m_cacheStorage.get(), std::forward(keys)); - } + co_await fillMissingValues(view.m_backendStorage.get(), keys, values); + co_return values; + } + friend auto tag_invoke(storage2::tag_t /*unused*/, View& view, + RANGES::input_range auto&& keys, storage2::DIRECT_TYPE /*unused*/) + -> task::Task>> + { + if (view.m_mutableStorage) + { co_return co_await storage2::readSome( - storage.m_backendStorage.get(), std::forward(keys)); + *view.m_mutableStorage, std::forward(keys)); } - friend auto tag_invoke( - storage2::tag_t /*unused*/, View& storage, auto&& key) - -> task::Task>> + for (auto& immutableStorage : view.m_immutableStorages) { - if (storage.m_mutableStorage) - { - if (auto value = co_await storage2::readOne(*storage.m_mutableStorage, key)) - { - co_return value; - } - } + co_return co_await storage2::readSome( + *immutableStorage, std::forward(keys)); + } - for (auto& immutableStorage : storage.m_immutableStorages) - { - if (auto value = co_await storage2::readOne(*immutableStorage, key)) - { - co_return value; - } - } + if constexpr (withCacheStorage) + { + co_return co_await storage2::readSome( + view.m_cacheStorage.get(), std::forward(keys)); + } + + co_return co_await storage2::readSome( + view.m_backendStorage.get(), std::forward(keys)); + } - if constexpr (withCacheStorage) + friend auto tag_invoke(storage2::tag_t /*unused*/, View& view, auto&& key) + -> task::Task>> + { + if (view.m_mutableStorage) + { + if (auto value = co_await storage2::readOne(*view.m_mutableStorage, key)) { - if (auto value = co_await storage2::readOne(storage.m_cacheStorage.get(), key)) - { - co_return value; - } + co_return value; } - - co_return co_await storage2::readOne(storage.m_backendStorage.get(), key); } - friend auto tag_invoke(storage2::tag_t /*unused*/, View& storage, - auto&& key, storage2::DIRECT_TYPE /*unused*/) - -> task::Task>> + for (auto& immutableStorage : view.m_immutableStorages) { - if (storage.m_mutableStorage) + if (auto value = co_await storage2::readOne(*immutableStorage, key)) { - co_return co_await storage2::readOne( - *storage.m_mutableStorage, std::forward(key)); + co_return value; } + } - for (auto& immutableStorage : storage.m_immutableStorages) + if constexpr (withCacheStorage) + { + if (auto value = co_await storage2::readOne(view.m_cacheStorage.get(), key)) { - co_return co_await storage2::readOne( - *immutableStorage, std::forward(key)); + co_return value; } + } - if constexpr (withCacheStorage) - { - co_return co_await storage2::readOne( - storage.m_cacheStorage.get(), std::forward(key)); - } + co_return co_await storage2::readOne(view.m_backendStorage.get(), key); + } + friend auto tag_invoke(storage2::tag_t /*unused*/, View& view, auto&& key, + storage2::DIRECT_TYPE /*unused*/) + -> task::Task>> + { + if (view.m_mutableStorage) + { co_return co_await storage2::readOne( - storage.m_backendStorage.get(), std::forward(key)); + *view.m_mutableStorage, std::forward(key)); } - friend task::Task tag_invoke(storage2::tag_t /*unused*/, - View& storage, RANGES::input_range auto&& keys, RANGES::input_range auto&& values) + for (auto& immutableStorage : view.m_immutableStorages) { - co_await storage2::writeSome(storage.mutableStorage(), - std::forward(keys), std::forward(values)); + co_return co_await storage2::readOne( + *immutableStorage, std::forward(key)); } - friend auto tag_invoke(storage2::tag_t /*unused*/, View& storage, - auto&& key, auto&& value) -> task::Task + if constexpr (withCacheStorage) { - co_await storage2::writeOne(storage.mutableStorage(), std::forward(key), - std::forward(value)); + co_return co_await storage2::readOne( + view.m_cacheStorage.get(), std::forward(key)); } - friend task::Task tag_invoke( - storage2::tag_t /*unused*/, View& toStorage, auto&& fromStorage) - { - co_await storage2::merge( - toStorage.mutableStorage(), std::forward(fromStorage)); - } + co_return co_await storage2::readOne( + view.m_backendStorage.get(), std::forward(key)); + } - friend task::Task tag_invoke(storage2::tag_t /*unused*/, - View& storage, RANGES::input_range auto&& keys, auto&&... args) - { - co_await storage2::removeSome(storage.mutableStorage(), - std::forward(keys), std::forward(args)...); - } + friend task::Task tag_invoke(storage2::tag_t /*unused*/, View& view, + RANGES::input_range auto&& keys, RANGES::input_range auto&& values) + { + co_await storage2::writeSome(view.mutableStorage(), std::forward(keys), + std::forward(values)); + } + + friend auto tag_invoke(storage2::tag_t /*unused*/, View& view, auto&& key, + auto&& value) -> task::Task + { + co_await storage2::writeOne(view.mutableStorage(), std::forward(key), + std::forward(value)); + } - class Iterator + friend task::Task tag_invoke( + storage2::tag_t /*unused*/, View& toView, auto&& fromStorage) + { + co_await storage2::merge( + toView.mutableStorage(), std::forward(fromStorage)); + } + + friend task::Task tag_invoke(storage2::tag_t /*unused*/, View& view, + RANGES::input_range auto&& keys, auto&&... args) + { + co_await storage2::removeSome(view.mutableStorage(), std::forward(keys), + std::forward(args)...); + } + + class Iterator + { + private: + using StorageIterator = + std::variant>>, + task::AwaitableReturnType>>>; + using RangeValue = + std::optional>; + std::vector> m_iterators; + + task::Task forwardIterators(auto&& iterators) { - private: - using StorageIterator = - std::variant>>, - task::AwaitableReturnType>>>; - using RangeValue = std::optional< - std::tuple>; - std::vector> m_iterators; - - task::Task forwardIterators(auto&& iterators) + for (auto& it : iterators) { - for (auto& it : iterators) - { - auto& [variantIterator, item] = it; - item = co_await std::visit( - [](auto& input) -> task::Task { + auto& [variantIterator, item] = it; + item = co_await std::visit( + bcos::recursiveLambda( + [&](auto const& self, auto& input) -> task::Task { RangeValue item; auto rangeValue = co_await input.next(); if (rangeValue) { auto&& [key, value] = *rangeValue; - if constexpr (std::is_pointer_v) + if constexpr (std::is_pointer_v>) { + if (!value) + { + co_return co_await self(self, input); + } item.emplace(key, *value); } else @@ -300,274 +276,249 @@ class MultiLayerStorage } } co_return item; - }, - variantIterator); - } + }), + variantIterator); } + } - public: - task::Task init(View& view, auto&&... args) + public: + task::Task init(View& view, auto&&... args) + { + if (view.m_mutableStorage) { - if (view.m_mutableStorage) - { - m_iterators.emplace_back(co_await storage2::range(*view.m_mutableStorage, - std::forward(args)...), - RangeValue{}); - } - for (auto& storage : view.m_immutableStorages) - { - m_iterators.emplace_back( - co_await storage2::range(*storage, std::forward(args)...), - RangeValue{}); - } - m_iterators.emplace_back(co_await storage2::range(view.m_backendStorage.get(), + m_iterators.emplace_back(co_await storage2::range(*view.m_mutableStorage, std::forward(args)...), RangeValue{}); - co_await forwardIterators(m_iterators); + } + for (auto& storage : view.m_immutableStorages) + { + m_iterators.emplace_back( + co_await storage2::range(*storage, std::forward(args)...), + RangeValue{}); + } + m_iterators.emplace_back(co_await storage2::range(view.m_backendStorage.get(), + std::forward(args)...), + RangeValue{}); + co_await forwardIterators(m_iterators); + } + + task::Task next() + { + // 基于合并排序,找到所有迭代器的最小值,推进迭代器并返回值 + // Based on merge sort, find the minimum value of all iterators, advance the + // iterator and return its value + auto iterators = m_iterators | RANGES::views::filter([](auto const& rangeValue) { + return std::get<1>(rangeValue).has_value(); + }); + if (RANGES::empty(iterators)) + { + co_return std::nullopt; } - task::Task next() + std::vector*> minIterators; + for (auto& it : iterators) { - // 基于合并排序,找到所有迭代器的最小值,推进迭代器并返回值 - // Based on merge sort, find the minimum value of all iterators, advance the - // iterator and return its value - auto iterators = m_iterators | RANGES::views::filter([](auto const& rangeValue) { - return std::get<1>(rangeValue).has_value(); - }); - if (RANGES::empty(iterators)) + if (minIterators.empty()) { - co_return std::nullopt; + minIterators.emplace_back(std::addressof(it)); } - - std::vector*> minIterators; - for (auto& it : iterators) + else { - if (minIterators.empty()) + auto& [variantIterator, value] = it; + auto& key = std::get<0>(*value); + auto& existsKey = std::get<0>(*std::get<1>(*minIterators[0])); + + if (key < existsKey) { + minIterators.clear(); minIterators.emplace_back(std::addressof(it)); } - else + else if (key == existsKey) { - auto& [variantIterator, value] = it; - auto& key = std::get<0>(*value); - auto& existsKey = std::get<0>(*std::get<1>(*minIterators[0])); - - if (key < existsKey) - { - minIterators.clear(); - minIterators.emplace_back(std::addressof(it)); - } - else if (key == existsKey) - { - minIterators.emplace_back(std::addressof(it)); - } + minIterators.emplace_back(std::addressof(it)); } } + } - RangeValue result = std::get<1>(*minIterators[0]); - co_await forwardIterators( - minIterators | - RANGES::views::transform([](auto* iterator) -> auto& { return *iterator; })); - co_return result; - }; + RangeValue result = std::get<1>(*minIterators[0]); + co_await forwardIterators( + minIterators | + RANGES::views::transform([](auto* iterator) -> auto& { return *iterator; })); + co_return result; }; + }; - friend task::Task tag_invoke( - bcos::storage2::tag_t /*unused*/, View& storage, auto&&... args) - { - Iterator iterator; - co_await iterator.init(storage, std::forward(args)...); - co_return iterator; - } + friend task::Task tag_invoke( + bcos::storage2::tag_t /*unused*/, View& view, auto&&... args) + { + Iterator iterator; + co_await iterator.init(view, std::forward(args)...); + co_return iterator; + } - MutableStorageType& mutableStorage() + MutableStorageType& mutableStorage() + { + if (!m_mutableStorage) { - if (!m_mutableStorage) - { - BOOST_THROW_EXCEPTION(NotExistsMutableStorageError{}); - } - return *m_mutableStorage; + BOOST_THROW_EXCEPTION(NotExistsMutableStorageError{}); } + return *m_mutableStorage; + } - View(const View&) = delete; - View& operator=(const View&) = delete; - View(View&&) noexcept = default; - View& operator=(View&&) noexcept = default; - ~View() noexcept { release(); } + View(const View&) = delete; + View& operator=(const View&) = delete; + View(View&&) noexcept = default; + View& operator=(View&&) noexcept = default; + ~View() noexcept = default; - using Key = KeyType; - using Value = ValueType; + using Key = KeyType; + using Value = ValueType; - void release() + template + void newMutable(Args&&... args) + { + if (m_mutableStorage) { - if (m_mutableLock.owns_lock()) - { - m_mutableLock.unlock(); - } + BOOST_THROW_EXCEPTION(DuplicateMutableStorageError{}); } - template - void newTemporaryMutable(Args... args) - { - if (m_mutableStorage) - { - BOOST_THROW_EXCEPTION(DuplicateMutableStorageError{}); - } + m_mutableStorage = + std::make_shared(std::forward(args)...); + } - m_mutableStorage = std::make_shared(args...); - } + BackendStorage& backendStorage() & { return m_backendStorage; } +}; - BackendStorage& backendStorage() & { return m_backendStorage; } - }; +template + requires((std::is_void_v || (!std::is_void_v))) +class MultiLayerStorage +{ +private: + constexpr static bool withCacheStorage = !std::is_void_v; + using KeyType = std::remove_cvref_t; + using ValueType = std::remove_cvref_t; + using ViewType = View; + + std::deque> m_storages; + std::mutex m_listMutex; + std::mutex m_mergeMutex; + + std::reference_wrapper> m_backendStorage; + [[no_unique_address]] std::conditional_t>, std::monostate> + m_cacheStorage; + +public: + using MutableStorage = MutableStorageType; using Key = KeyType; using Value = ValueType; - explicit MultiLayerStorage(BackendStorage& backendStorage) + explicit MultiLayerStorage(BackendStorage& backendStorage) noexcept requires(!withCacheStorage) : m_backendStorage(backendStorage) - {} + { + static_assert(std::same_as); + static_assert( + std::same_as); + } MultiLayerStorage(BackendStorage& backendStorage, std::conditional_t, std::monostate> - cacheStorage) + cacheStorage) noexcept requires(withCacheStorage) : m_backendStorage(backendStorage), m_cacheStorage(cacheStorage) - {} + { + static_assert(std::same_as); + static_assert( + std::same_as); + } MultiLayerStorage(const MultiLayerStorage&) = delete; - MultiLayerStorage(MultiLayerStorage&&) noexcept = delete; + MultiLayerStorage(MultiLayerStorage&&) noexcept = default; MultiLayerStorage& operator=(const MultiLayerStorage&) = delete; - MultiLayerStorage& operator=(MultiLayerStorage&&) noexcept = delete; + MultiLayerStorage& operator=(MultiLayerStorage&&) noexcept = default; ~MultiLayerStorage() noexcept = default; - View fork(bool withMutable) + ViewType fork() { std::unique_lock lock(m_listMutex); if constexpr (withCacheStorage) { - View view(m_backendStorage, m_cacheStorage); - if (withMutable) - { - view.m_mutableLock = {m_mutableMutex, std::try_to_lock}; - if (!view.m_mutableLock.owns_lock()) - { - BOOST_THROW_EXCEPTION(DuplicateMutableViewError{}); - } - view.m_mutableStorage = m_mutableStorage; - } - view.m_immutableStorages = m_immutableStorages; - + ViewType view(m_backendStorage, m_cacheStorage); + view.m_immutableStorages = m_storages; return view; } else { - View view(m_backendStorage); - if (withMutable) - { - view.m_mutableLock = {m_mutableMutex, std::try_to_lock}; - if (!view.m_mutableLock.owns_lock()) - { - BOOST_THROW_EXCEPTION(DuplicateMutableViewError{}); - } - view.m_mutableStorage = m_mutableStorage; - } - view.m_immutableStorages = m_immutableStorages; - + ViewType view(m_backendStorage); + view.m_immutableStorages = m_storages; return view; } } - template - void newMutable(Args... args) - { - std::unique_lock lock(m_listMutex); - if (m_mutableStorage) - { - BOOST_THROW_EXCEPTION(DuplicateMutableStorageError{}); - } - - m_mutableStorage = std::make_shared(args...); - } - - void removeMutable() - { - std::unique_lock lock(m_listMutex); - m_mutableStorage.reset(); - } - - void pushMutableToImmutableFront() + void pushView(ViewType&& view) { - if (!m_mutableStorage) + if (!view.m_mutableStorage) { - BOOST_THROW_EXCEPTION(NotExistsMutableStorageError{}); + return; } std::unique_lock lock(m_listMutex); - m_immutableStorages.push_front(std::move(m_mutableStorage)); - m_mutableStorage.reset(); + m_storages.push_front(std::move(view.m_mutableStorage)); } - task::Task> mergeAndPopImmutableBack() + task::Task> mergeBackStorage() { std::unique_lock mergeLock(m_mergeMutex); - std::unique_lock immutablesLock(m_listMutex); - if (m_immutableStorages.empty()) + std::unique_lock listLock(m_listMutex); + if (m_storages.empty()) { BOOST_THROW_EXCEPTION(NotExistsImmutableStorageError{}); } - auto immutableStorage = m_immutableStorages.back(); - immutablesLock.unlock(); + auto backStoragePtr = m_storages.back(); + auto const& backStorage = *backStoragePtr; + listLock.unlock(); if constexpr (withCacheStorage) { tbb::parallel_invoke( [&]() { - task::syncWait(storage2::merge(m_backendStorage.get(), *immutableStorage)); + task::tbb::syncWait(storage2::merge(m_backendStorage.get(), backStorage)); }, - [&]() { - task::syncWait(storage2::merge(m_cacheStorage.get(), *immutableStorage)); - }); + [&]() { task::tbb::syncWait(storage2::merge(m_cacheStorage.get(), backStorage)); }); } else { - co_await storage2::merge(m_backendStorage.get(), *immutableStorage); + co_await storage2::merge(m_backendStorage.get(), backStorage); } - immutablesLock.lock(); - m_immutableStorages.pop_back(); + listLock.lock(); + m_storages.pop_back(); - co_return immutableStorage; + co_return backStoragePtr; } - std::shared_ptr frontImmutableStorage() + std::shared_ptr frontStorage() { std::unique_lock immutablesLock(m_listMutex); - if (m_immutableStorages.empty()) + if (m_storages.empty()) { BOOST_THROW_EXCEPTION(NotExistsImmutableStorageError{}); } - return m_immutableStorages.front(); + return m_storages.front(); } - std::shared_ptr lastImmutableStorage() + std::shared_ptr backStorage() { std::unique_lock immutablesLock(m_listMutex); - if (m_immutableStorages.empty()) + if (m_storages.empty()) { BOOST_THROW_EXCEPTION(NotExistsImmutableStorageError{}); } - return m_immutableStorages.back(); + return m_storages.back(); } - MutableStorageType& mutableStorage() - { - if (!m_mutableStorage) - { - BOOST_THROW_EXCEPTION(NotExistsMutableStorageError{}); - } - return *m_mutableStorage; - } BackendStorage& backendStorage() { return m_backendStorage; } }; diff --git a/transaction-scheduler/bcos-transaction-scheduler/SchedulerParallelImpl.h b/transaction-scheduler/bcos-transaction-scheduler/SchedulerParallelImpl.h index 36d872d880..d1d29d3aa0 100644 --- a/transaction-scheduler/bcos-transaction-scheduler/SchedulerParallelImpl.h +++ b/transaction-scheduler/bcos-transaction-scheduler/SchedulerParallelImpl.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -26,112 +27,123 @@ namespace bcos::transaction_scheduler #define PARALLEL_SCHEDULER_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("PARALLEL_SCHEDULER") -class SchedulerParallelImpl +template +struct StorageTrait +{ + using LocalStorageView = View; + using LocalReadWriteSetStorage = + ReadWriteSetStorage; +}; + +template +struct ExecutionContext +{ + ExecutionContext(int contextID, std::reference_wrapper transaction, + std::reference_wrapper receipt, + std::optional coro, typename CoroType::Iterator iterator) + : contextID(contextID), + transaction(transaction), + receipt(receipt), + coro(std::move(coro)), + iterator(std::move(iterator)) + {} + int contextID; + std::reference_wrapper transaction; + std::reference_wrapper receipt; + std::optional coro; + typename CoroType::Iterator iterator; +}; + +template +class ChunkStatus { private: - template - struct StorageTrait - { - using LocalStorage = MultiLayerStorage; - using LocalStorageView = - std::invoke_result_t; - using LocalReadWriteSetStorage = - ReadWriteSetStorage; - }; - - template - class ChunkStatus + int64_t m_chunkIndex = 0; + std::reference_wrapper m_hasRAW; + ContextRange m_contextRange; + std::reference_wrapper m_executor; + typename StorageTrait::LocalStorageView m_storageView; + typename StorageTrait::LocalReadWriteSetStorage m_readWriteSetStorage; + +public: + ChunkStatus(int64_t chunkIndex, boost::atomic_flag const& hasRAW, ContextRange contextRange, + Executor& executor, auto& storage) + : m_chunkIndex(chunkIndex), + m_hasRAW(hasRAW), + m_contextRange(std::move(contextRange)), + m_executor(executor), + m_storageView(storage), + m_readWriteSetStorage(m_storageView) { - private: - auto forkAndMutable(auto& storage) - { - storage.newMutable(); - auto view = storage.fork(true); - return view; - } + m_storageView.newMutable(); + } - int64_t m_chunkIndex = 0; - std::reference_wrapper m_hasRAW; - ContextRange m_contextRange; - std::reference_wrapper m_executor; - typename StorageTrait::LocalStorage m_localStorage; - typename StorageTrait::LocalStorageView m_localStorageView; - typename StorageTrait::LocalReadWriteSetStorage m_localReadWriteSetStorage; - - public: - ChunkStatus(int64_t chunkIndex, boost::atomic_flag const& hasRAW, ContextRange contextRange, - Executor& executor, auto& storage) - : m_chunkIndex(chunkIndex), - m_hasRAW(hasRAW), - m_contextRange(std::move(contextRange)), - m_executor(executor), - m_localStorage(storage), - m_localStorageView(forkAndMutable(m_localStorage)), - m_localReadWriteSetStorage(m_localStorageView) - {} - - int64_t chunkIndex() const { return m_chunkIndex; } - auto count() const { return RANGES::size(m_contextRange); } - auto& localStorage() & { return m_localStorage; } - auto& readWriteSetStorage() & { return m_localReadWriteSetStorage; } - - void executeStep1( - protocol::BlockHeader const& blockHeader, ledger::LedgerConfig const& ledgerConfig) + int64_t chunkIndex() const { return m_chunkIndex; } + auto count() const { return RANGES::size(m_contextRange); } + auto& storageView() & { return m_storageView; } + auto& readWriteSetStorage() & { return m_readWriteSetStorage; } + + void executeStep1( + protocol::BlockHeader const& blockHeader, ledger::LedgerConfig const& ledgerConfig) + { + ittapi::Report report(ittapi::ITT_DOMAINS::instance().PARALLEL_SCHEDULER, + ittapi::ITT_DOMAINS::instance().EXECUTE_CHUNK1); + for (auto&& [index, context] : RANGES::views::enumerate(m_contextRange)) { - ittapi::Report report(ittapi::ITT_DOMAINS::instance().PARALLEL_SCHEDULER, - ittapi::ITT_DOMAINS::instance().EXECUTE_CHUNK1); - for (auto&& [index, context] : RANGES::views::enumerate(m_contextRange)) + if (m_hasRAW.get().test()) { - if (m_hasRAW.get().test()) - { - PARALLEL_SCHEDULER_LOG(DEBUG) - << "Chunk: " << m_chunkIndex << " aborted in step1, executed " << index - << " transactions"; - break; - } - - context.coro.emplace(transaction_executor::execute3Step(m_executor.get(), - m_localReadWriteSetStorage, blockHeader, context.transaction.get(), - context.contextID, ledgerConfig, task::tbb::syncWait)); - context.iterator = context.coro->begin(); - context.receipt.get() = *context.iterator; + PARALLEL_SCHEDULER_LOG(DEBUG) + << "Chunk: " << m_chunkIndex << " aborted in step1, executed " << index + << " transactions"; + break; } + + context.coro.emplace(transaction_executor::execute3Step(m_executor.get(), + m_readWriteSetStorage, blockHeader, context.transaction.get(), context.contextID, + ledgerConfig, task::tbb::syncWait)); + context.iterator = context.coro->begin(); + context.receipt.get() = *context.iterator; } + } - void executeStep2() + void executeStep2() + { + ittapi::Report report(ittapi::ITT_DOMAINS::instance().PARALLEL_SCHEDULER, + ittapi::ITT_DOMAINS::instance().EXECUTE_CHUNK2); + for (auto&& [index, context] : RANGES::views::enumerate(m_contextRange)) { - ittapi::Report report(ittapi::ITT_DOMAINS::instance().PARALLEL_SCHEDULER, - ittapi::ITT_DOMAINS::instance().EXECUTE_CHUNK2); - for (auto&& [index, context] : RANGES::views::enumerate(m_contextRange)) + if (m_hasRAW.get().test()) + { + PARALLEL_SCHEDULER_LOG(DEBUG) + << "Chunk: " << m_chunkIndex << " aborted in step2, executed " << index + << " transactions"; + break; + } + if (!context.receipt.get() && context.iterator != context.coro->end()) { - if (m_hasRAW.get().test()) - { - PARALLEL_SCHEDULER_LOG(DEBUG) - << "Chunk: " << m_chunkIndex << " aborted in step2, executed " << index - << " transactions"; - break; - } - if (!context.receipt.get() && context.iterator != context.coro->end()) - { - context.receipt.get() = *(++context.iterator); - } + context.receipt.get() = *(++context.iterator); } } + } - void executeStep3() + void executeStep3() + { + ittapi::Report report(ittapi::ITT_DOMAINS::instance().PARALLEL_SCHEDULER, + ittapi::ITT_DOMAINS::instance().EXECUTE_CHUNK3); + for (auto& context : m_contextRange) { - ittapi::Report report(ittapi::ITT_DOMAINS::instance().PARALLEL_SCHEDULER, - ittapi::ITT_DOMAINS::instance().EXECUTE_CHUNK3); - for (auto& context : m_contextRange) + if (!context.receipt.get() && context.iterator != context.coro->end()) { - if (!context.receipt.get() && context.iterator != context.coro->end()) - { - context.receipt.get() = *(++context.iterator); - } + context.receipt.get() = *(++context.iterator); } } - }; + } +}; +template +class SchedulerParallelImpl +{ +private: constexpr static auto DEFAULT_TRANSACTION_GRAIN_SIZE = 16L; GC m_gc; size_t m_grainSize = DEFAULT_TRANSACTION_GRAIN_SIZE; @@ -155,18 +167,18 @@ class SchedulerParallelImpl const auto count = RANGES::size(contexts); ReadWriteSetStorage writeSet(storage); - using Chunk = SchedulerParallelImpl::ChunkStatus, + using Chunk = ChunkStatus, std::decay_t, decltype(RANGES::subrange>(contexts))>; - using ChunkStorage = typename std::decay_t::MutableStorage; boost::atomic_flag hasRAW; - ChunkStorage lastStorage; - const auto contextChunks = RANGES::views::chunk(contexts, chunkSize); + MutableStorage lastStorage; + auto contextChunks = RANGES::views::chunk(contexts, chunkSize); std::atomic_size_t offset = 0; std::atomic_size_t chunkIndex = 0; + tbb::task_group_context context; // 五级流水线:分片准备、并行执行、检测RAW冲突&合并读写集、生成回执、合并storage // Five-stage pipeline: shard preparation, parallel execution, detection of RAW // conflicts & merging read/write sets, generating receipts, and merging storage @@ -246,8 +258,8 @@ class SchedulerParallelImpl return chunk; }) & - tbb::make_filter, void>( - tbb::filter_mode::serial_in_order, [&](std::unique_ptr chunk) { + tbb::make_filter, void>(tbb::filter_mode::serial_in_order, + [&](std::unique_ptr chunk) { if (chunk) { ittapi::Report report1( @@ -261,10 +273,15 @@ class SchedulerParallelImpl << "Merging storage... " << chunk->chunkIndex() << " | " << chunk->count(); task::tbb::syncWait(storage2::merge( - lastStorage, std::move(chunk->localStorage().mutableStorage()))); + lastStorage, std::move(chunk->storageView().mutableStorage()))); scheduler.m_gc.collect(std::move(chunk)); } - })); + else + { + context.cancel_group_execution(); + } + }), + context); task::tbb::syncWait(mergeLastStorage(scheduler, storage, std::move(lastStorage))); scheduler.m_gc.collect(std::move(writeSet)); @@ -280,16 +297,6 @@ class SchedulerParallelImpl return 0; } - template - struct ExecutionContext - { - int contextID{}; - std::reference_wrapper transaction; - std::reference_wrapper receipt; - std::optional coro; - typename CoroType::Iterator iterator; - }; - friend task::Task> tag_invoke( tag_t /*unused*/, SchedulerParallelImpl& scheduler, auto& storage, auto& executor, protocol::BlockHeader const& blockHeader, @@ -303,22 +310,20 @@ class SchedulerParallelImpl std::vector receipts(count); using Storage = std::decay_t; - using CoroType = std::invoke_result_t::LocalReadWriteSetStorage>, - protocol::BlockHeader const&, protocol::Transaction const&, int, - ledger::LedgerConfig const&, task::tbb::SyncWait>; + using CoroType = + std::invoke_result_t::LocalReadWriteSetStorage>, + protocol::BlockHeader const&, protocol::Transaction const&, int, + ledger::LedgerConfig const&, task::tbb::SyncWait>; std::vector, tbb::cache_aligned_allocator>> contexts; contexts.reserve(RANGES::size(transactions)); for (auto index : RANGES::views::iota(0, (int)RANGES::size(transactions))) { - contexts.emplace_back(ExecutionContext{.contextID = index, - .transaction = transactions[index], - .receipt = receipts[index], - .coro = {}, - .iterator = {}}); + contexts.emplace_back( + ExecutionContext{index, transactions[index], receipts[index], {}, {}}); } constexpr static auto DEFAULT_PARALLEL_ARENA = 8; diff --git a/transaction-scheduler/benchmark/benchmarkMultiLayerStorage.cpp b/transaction-scheduler/benchmark/benchmarkMultiLayerStorage.cpp index 5b4eb67e24..32355d3470 100644 --- a/transaction-scheduler/benchmark/benchmarkMultiLayerStorage.cpp +++ b/transaction-scheduler/benchmark/benchmarkMultiLayerStorage.cpp @@ -18,11 +18,10 @@ struct Fixture void prepareData(int64_t count, int layer = 0) { - multiLayerStorage.newMutable(); - // Write count data task::syncWait([this](int64_t count) -> task::Task { - auto view = multiLayerStorage.fork(true); + auto view = multiLayerStorage.fork(); + view.newMutable(); allKeys = RANGES::views::iota(0, count) | RANGES::views::transform([](int num) { auto key = fmt::format("key: {}", num); return transaction_executor::StateKey{"test_table"sv, std::string_view(key)}; @@ -36,12 +35,14 @@ struct Fixture }); co_await storage2::writeSome(view, allKeys, allValues); + multiLayerStorage.pushView(std::move(view)); }(count)); for (auto i = 0; i < layer; ++i) { - multiLayerStorage.pushMutableToImmutableFront(); - multiLayerStorage.newMutable(); + auto view = multiLayerStorage.fork(); + view.newMutable(); + multiLayerStorage.pushView(std::move(view)); } } @@ -64,7 +65,7 @@ static void read1(benchmark::State& state) int i = 0; task::syncWait([&](benchmark::State& state) -> task::Task { - auto view = fixture.multiLayerStorage.fork(false); + auto view = fixture.multiLayerStorage.fork(); for (auto const& it : state) { [[maybe_unused]] auto data = @@ -84,7 +85,7 @@ static void read10(benchmark::State& state) int i = 0; task::syncWait([&](benchmark::State& state) -> task::Task { - auto view = fixture.multiLayerStorage.fork(false); + auto view = fixture.multiLayerStorage.fork(); for (auto const& it : state) { [[maybe_unused]] auto data = @@ -99,11 +100,11 @@ static void read10(benchmark::State& state) static void write1(benchmark::State& state) { Fixture fixture; - fixture.multiLayerStorage.newMutable(); int i = 0; task::syncWait([&](benchmark::State& state) -> task::Task { - auto view = fixture.multiLayerStorage.fork(true); + auto view = fixture.multiLayerStorage.fork(); + view.newMutable(); for (auto const& it : state) { storage::Entry entry; diff --git a/transaction-scheduler/benchmark/benchmarkScheduler.cpp b/transaction-scheduler/benchmark/benchmarkScheduler.cpp index a6bfdcd7d3..1223a1db56 100644 --- a/transaction-scheduler/benchmark/benchmarkScheduler.cpp +++ b/transaction-scheduler/benchmark/benchmarkScheduler.cpp @@ -52,7 +52,7 @@ struct Fixture if constexpr (parallel) { - m_scheduler.emplace(); + m_scheduler.emplace>(); } else { @@ -91,8 +91,8 @@ struct Fixture RANGES::single_view(std::addressof(createTransaction)) | RANGES::views::transform([](auto* ptr) -> auto const& { return *ptr; }); - m_multiLayerStorage.newMutable(); - auto view = m_multiLayerStorage.fork(true); + auto view = m_multiLayerStorage.fork(); + view.newMutable(); ledger::LedgerConfig ledgerConfig; auto receipts = co_await transaction_scheduler::executeBlock(scheduler, view, @@ -103,8 +103,8 @@ struct Fixture receipts[0]->status(), receipts[0]->message()); co_return; } - m_multiLayerStorage.pushMutableToImmutableFront(); - co_await m_multiLayerStorage.mergeAndPopImmutableBack(); + m_multiLayerStorage.pushView(std::move(view)); + co_await m_multiLayerStorage.mergeBackStorage(); m_contractAddress = receipts[0]->contractAddress(); }()); @@ -233,7 +233,8 @@ struct Fixture RANGES::to< std::vector>>(); - auto view = m_multiLayerStorage.fork(true); + auto view = m_multiLayerStorage.fork(); + view.newMutable(); ledger::LedgerConfig ledgerConfig; auto receipts = co_await transaction_scheduler::executeBlock(scheduler, view, m_executor, blockHeader, @@ -275,7 +276,8 @@ struct Fixture bcos::bytes m_helloworldBytecodeBinary; TransactionExecutorImpl m_executor; - std::variant m_scheduler; + std::variant> + m_scheduler; std::string m_contractAddress; std::vector
m_addresses; @@ -302,8 +304,8 @@ static void issue(benchmark::State& state) else { task::syncWait([&](benchmark::State& state) -> task::Task { - fixture.m_multiLayerStorage.newMutable(true); - auto view = fixture.m_multiLayerStorage.fork(true); + auto view = fixture.m_multiLayerStorage.fork(); + view.newMutable(); for (auto const& it : state) { bcostars::protocol::BlockHeaderImpl blockHeader( @@ -325,7 +327,7 @@ static void issue(benchmark::State& state) ledgerConfig); } - view.release(); + fixture.m_multiLayerStorage.pushView(std::move(view)); auto balances = co_await fixture.balances(); for (auto& balance : balances) { @@ -336,7 +338,7 @@ static void issue(benchmark::State& state) balance.template convert_to()))); } } - fixture.m_multiLayerStorage.pushMutableToImmutableFront(); + co_await fixture.m_multiLayerStorage.mergeBackStorage(); }(state)); } }, @@ -371,8 +373,8 @@ static void transfer(benchmark::State& state) blockHeader.setNumber(0); blockHeader.setVersion((uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION); - fixture.m_multiLayerStorage.newMutable(); - auto view = fixture.m_multiLayerStorage.fork(true); + auto view = fixture.m_multiLayerStorage.fork(); + view.newMutable(); ledger::LedgerConfig ledgerConfig; [[maybe_unused]] auto receipts = co_await transaction_scheduler::executeBlock( scheduler, view, fixture.m_executor, blockHeader, @@ -408,7 +410,7 @@ static void transfer(benchmark::State& state) } // Check - view.release(); + fixture.m_multiLayerStorage.pushView(std::move(view)); auto balances = co_await fixture.balances(); for (auto&& range : balances | RANGES::views::chunk(2)) { @@ -429,8 +431,7 @@ static void transfer(benchmark::State& state) to.template convert_to()))); } } - - fixture.m_multiLayerStorage.pushMutableToImmutableFront(); + co_await fixture.m_multiLayerStorage.mergeBackStorage(); }(state)); } }, @@ -456,8 +457,8 @@ static void conflictTransfer(benchmark::State& state) else { int i = 0; - fixture.m_multiLayerStorage.newMutable(); - auto view = fixture.m_multiLayerStorage.fork(true); + auto view = fixture.m_multiLayerStorage.fork(); + view.newMutable(); task::syncWait([&](benchmark::State& state) -> task::Task { // First issue @@ -502,7 +503,7 @@ static void conflictTransfer(benchmark::State& state) } // Check - view.release(); + fixture.m_multiLayerStorage.pushView(std::move(view)); auto balances = co_await fixture.balances(); for (auto&& [balance, index] : RANGES::views::zip(balances, RANGES::views::iota(0LU))) @@ -535,7 +536,7 @@ static void conflictTransfer(benchmark::State& state) } } } - fixture.m_multiLayerStorage.pushMutableToImmutableFront(); + co_await fixture.m_multiLayerStorage.mergeBackStorage(); }(state)); } }, diff --git a/transaction-scheduler/tests/testMultiLayerStorage.cpp b/transaction-scheduler/tests/testMultiLayerStorage.cpp index d508fb00ad..30ed4a58bf 100644 --- a/transaction-scheduler/tests/testMultiLayerStorage.cpp +++ b/transaction-scheduler/tests/testMultiLayerStorage.cpp @@ -35,7 +35,7 @@ BOOST_FIXTURE_TEST_SUITE(TestMultiLayerStorage, TestMultiLayerStorageFixture) BOOST_AUTO_TEST_CASE(noMutable) { task::syncWait([this]() -> task::Task { - auto view = multiLayerStorage.fork(true); + auto view = multiLayerStorage.fork(); storage::Entry entry; BOOST_CHECK_THROW(co_await storage2::writeOne( view, StateKey{"test_table"sv, "test_key"sv}, std::move(entry)), @@ -48,11 +48,8 @@ BOOST_AUTO_TEST_CASE(noMutable) BOOST_AUTO_TEST_CASE(readWriteMutable) { task::syncWait([this]() -> task::Task { - BOOST_CHECK_THROW( - multiLayerStorage.pushMutableToImmutableFront(), NotExistsMutableStorageError); - - multiLayerStorage.newMutable(); - auto view = std::make_optional(multiLayerStorage.fork(true)); + auto view = std::make_optional(multiLayerStorage.fork()); + view->newMutable(); StateKey key{"test_table"sv, "test_key"sv}; storage::Entry entry; @@ -63,10 +60,9 @@ BOOST_AUTO_TEST_CASE(readWriteMutable) auto values = co_await storage2::readSome(*view, keyViews); BOOST_CHECK_EQUAL(values[0]->get(), entry.get()); + BOOST_CHECK_NO_THROW(multiLayerStorage.pushView(std::move(*view))); - BOOST_CHECK_NO_THROW(multiLayerStorage.pushMutableToImmutableFront()); - view.reset(); - auto view2 = multiLayerStorage.fork(true); + auto view2 = multiLayerStorage.fork(); BOOST_CHECK_THROW( co_await storage2::writeOne(view2, key, entry), NotExistsMutableStorageError); @@ -77,14 +73,10 @@ BOOST_AUTO_TEST_CASE(readWriteMutable) BOOST_AUTO_TEST_CASE(merge) { task::syncWait([this]() -> task::Task { - BOOST_CHECK_THROW( - multiLayerStorage.pushMutableToImmutableFront(), NotExistsMutableStorageError); - - multiLayerStorage.newMutable(); - auto view = std::make_optional(multiLayerStorage.fork(true)); - auto toKey = RANGES::views::transform([](int num) { - return StateKey{"test_table"sv, fmt::format("key: {}", num)}; - }); + auto view = std::make_optional(multiLayerStorage.fork()); + view->newMutable(); + auto toKey = RANGES::views::transform( + [](int num) { return StateKey{"test_table"sv, fmt::format("key: {}", num)}; }); auto toValue = RANGES::views::transform([](int num) { storage::Entry entry; entry.set(fmt::format("value: {}", num)); @@ -96,13 +88,12 @@ BOOST_AUTO_TEST_CASE(merge) RANGES::iota_view(0, 100) | toValue); BOOST_CHECK_THROW( - co_await multiLayerStorage.mergeAndPopImmutableBack(), NotExistsImmutableStorageError); + co_await multiLayerStorage.mergeBackStorage(), NotExistsImmutableStorageError); - multiLayerStorage.pushMutableToImmutableFront(); - co_await multiLayerStorage.mergeAndPopImmutableBack(); - view.reset(); + multiLayerStorage.pushView(std::move(*view)); + co_await multiLayerStorage.mergeBackStorage(); - auto view2 = multiLayerStorage.fork(false); + auto view2 = multiLayerStorage.fork(); auto keys = RANGES::iota_view(0, 100) | toKey; auto values = co_await storage2::readSome(view2, keys); @@ -112,12 +103,11 @@ BOOST_AUTO_TEST_CASE(merge) } BOOST_CHECK_EQUAL(RANGES::size(values), 100); - multiLayerStorage.newMutable(); - - auto view3 = multiLayerStorage.fork(true); + auto view3 = multiLayerStorage.fork(); + view3.newMutable(); co_await storage2::removeSome(view3, RANGES::iota_view(20, 30) | toKey); - multiLayerStorage.pushMutableToImmutableFront(); - co_await multiLayerStorage.mergeAndPopImmutableBack(); + multiLayerStorage.pushView(std::move(view3)); + co_await multiLayerStorage.mergeBackStorage(); auto values2 = co_await storage2::readSome(view3, keys); for (auto&& [index, value] : RANGES::views::enumerate(values2)) @@ -136,21 +126,10 @@ BOOST_AUTO_TEST_CASE(merge) }()); } -BOOST_AUTO_TEST_CASE(oneMutable) -{ - multiLayerStorage.newMutable(); - auto view1 = std::make_optional(multiLayerStorage.fork(true)); - auto view2 = multiLayerStorage.fork(false); - BOOST_CHECK_THROW(auto view3 = multiLayerStorage.fork(true), DuplicateMutableViewError); - - view1.reset(); - auto view4 = multiLayerStorage.fork(true); -} - BOOST_AUTO_TEST_CASE(rangeMulti) { - using MutableStorage = - memory_storage::MemoryStorage; + using MutableStorage = memory_storage::MemoryStorage; using BackendStorage = memory_storage::MemoryStorage; @@ -161,21 +140,21 @@ BOOST_AUTO_TEST_CASE(rangeMulti) MultiLayerStorage myMultiLayerStorage(backendStorage); - myMultiLayerStorage.newMutable(); - auto view1 = myMultiLayerStorage.fork(true); + auto view1 = myMultiLayerStorage.fork(); + view1.newMutable(); co_await storage2::writeSome(view1, RANGES::views::iota(2, 6), RANGES::views::repeat(1)); - view1.release(); - myMultiLayerStorage.pushMutableToImmutableFront(); + co_await storage2::removeOne(view1, 2); + myMultiLayerStorage.pushView(std::move(view1)); - myMultiLayerStorage.newMutable(); - auto view2 = myMultiLayerStorage.fork(true); + auto view2 = myMultiLayerStorage.fork(); + view2.newMutable(); co_await storage2::writeSome(view2, RANGES::views::iota(4, 8), RANGES::views::repeat(2)); auto resultList = co_await storage2::readSome(view2, RANGES::views::iota(0, 8)); auto vecList = resultList | RANGES::views::transform([](auto input) { return *input; }) | RANGES::to(); BOOST_CHECK_EQUAL(resultList.size(), 8); - auto expectList = std::vector({0, 0, 1, 1, 2, 2, 2, 2}); + auto expectList = std::vector({0, 0, 0, 1, 2, 2, 2, 2}); BOOST_CHECK_EQUAL_COLLECTIONS( vecList.begin(), vecList.end(), expectList.begin(), expectList.end()); diff --git a/transaction-scheduler/tests/testSchedulerParallel.cpp b/transaction-scheduler/tests/testSchedulerParallel.cpp index 4f9414e596..56899ac40d 100644 --- a/transaction-scheduler/tests/testSchedulerParallel.cpp +++ b/transaction-scheduler/tests/testSchedulerParallel.cpp @@ -74,7 +74,7 @@ BOOST_AUTO_TEST_CASE(simple) { task::syncWait([&, this]() -> task::Task { MockExecutor executor; - SchedulerParallelImpl scheduler; + SchedulerParallelImpl scheduler; bcostars::protocol::BlockHeaderImpl blockHeader( [inner = bcostars::BlockHeader()]() mutable { return std::addressof(inner); }); @@ -85,8 +85,8 @@ BOOST_AUTO_TEST_CASE(simple) }) | RANGES::to>>(); - multiLayerStorage.newMutable(); - auto view = multiLayerStorage.fork(true); + auto view = multiLayerStorage.fork(); + view.newMutable(); ledger::LedgerConfig ledgerConfig; auto receipts = co_await bcos::transaction_scheduler::executeBlock(scheduler, view, executor, blockHeader, @@ -139,16 +139,19 @@ BOOST_AUTO_TEST_CASE(conflict) { task::syncWait([&, this]() -> task::Task { MockConflictExecutor executor; - SchedulerParallelImpl scheduler; + SchedulerParallelImpl scheduler; + + auto view1 = multiLayerStorage.fork(); + view1.newMutable(); + multiLayerStorage.pushView(std::move(view1)); - multiLayerStorage.newMutable(); constexpr static int INITIAL_VALUE = 100000; for (auto i : RANGES::views::iota(0LU, MOCK_USER_COUNT)) { StateKey key{"t_test"sv, boost::lexical_cast(i)}; storage::Entry entry; entry.set(boost::lexical_cast(INITIAL_VALUE)); - co_await storage2::writeOne(multiLayerStorage.mutableStorage(), key, std::move(entry)); + co_await storage2::writeOne(*multiLayerStorage.frontStorage(), key, std::move(entry)); } bcostars::protocol::BlockHeaderImpl blockHeader( @@ -167,15 +170,17 @@ BOOST_AUTO_TEST_CASE(conflict) auto transactionRefs = transactions | RANGES::views::transform([](auto& ptr) -> auto& { return *ptr; }); - auto view = multiLayerStorage.fork(true); + auto view = multiLayerStorage.fork(); + view.newMutable(); ledger::LedgerConfig ledgerConfig; auto receipts = co_await bcos::transaction_scheduler::executeBlock( scheduler, view, executor, blockHeader, transactionRefs, ledgerConfig); + multiLayerStorage.pushView(std::move(view)); for (auto i : RANGES::views::iota(0LU, MOCK_USER_COUNT)) { StateKey key{"t_test"sv, boost::lexical_cast(i)}; - auto entry = co_await storage2::readOne(multiLayerStorage.mutableStorage(), key); + auto entry = co_await storage2::readOne(*multiLayerStorage.frontStorage(), key); BOOST_CHECK_EQUAL(boost::lexical_cast(entry->get()), INITIAL_VALUE); } for (auto const& receipt : receipts) diff --git a/transaction-scheduler/tests/testSchedulerSerial.cpp b/transaction-scheduler/tests/testSchedulerSerial.cpp index ca600531a4..0c4eb4c915 100644 --- a/transaction-scheduler/tests/testSchedulerSerial.cpp +++ b/transaction-scheduler/tests/testSchedulerSerial.cpp @@ -79,7 +79,8 @@ BOOST_AUTO_TEST_CASE(executeBlock) RANGES::to>>(); MockExecutor executor; - auto view = multiLayerStorage.fork(true); + auto view = multiLayerStorage.fork(); + view.newMutable(); ledger::LedgerConfig ledgerConfig; auto receipts = co_await bcos::transaction_scheduler::executeBlock(scheduler, view, executor, blockHeader, From 76b7c92dd8842e52dbee2d188924b52eb163f09e Mon Sep 17 00:00:00 2001 From: MO NAN <651932351@qq.com> Date: Wed, 29 May 2024 09:46:27 +0800 Subject: [PATCH 31/39] Fix bugfix_set_row_dirty_flag error (#4487) --- .../executor/ShardingTransactionExecutor.cpp | 8 ++++-- .../src/executor/TransactionExecutor.cpp | 28 +++++-------------- bcos-ledger/src/libledger/Ledger.h | 2 +- 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/bcos-executor/src/executor/ShardingTransactionExecutor.cpp b/bcos-executor/src/executor/ShardingTransactionExecutor.cpp index f45d4dd459..53a2f0b976 100644 --- a/bcos-executor/src/executor/ShardingTransactionExecutor.cpp +++ b/bcos-executor/src/executor/ShardingTransactionExecutor.cpp @@ -162,17 +162,19 @@ void ShardingTransactionExecutor::executeTransactions(std::string contractAddres BlockContext::Ptr ShardingTransactionExecutor::createTmpBlockContext( const protocol::BlockHeader::ConstPtr& currentHeader) { + ledger::Features features; bcos::storage::StateStorageInterface::Ptr stateStorage; - if (m_cachedStorage) { + task::syncWait(features.readFromStorage(*m_cachedStorage, currentHeader->number())); stateStorage = createStateStorage(m_cachedStorage, true, - m_blockContext->features().get(ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); + features.get(ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); } else { + task::syncWait(features.readFromStorage(*m_backendStorage, currentHeader->number())); stateStorage = createStateStorage(m_backendStorage, true, - m_blockContext->features().get(ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); + features.get(ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); } return createBlockContext(currentHeader, stateStorage); diff --git a/bcos-executor/src/executor/TransactionExecutor.cpp b/bcos-executor/src/executor/TransactionExecutor.cpp index b54682ede3..2ed03692f6 100644 --- a/bcos-executor/src/executor/TransactionExecutor.cpp +++ b/bcos-executor/src/executor/TransactionExecutor.cpp @@ -634,8 +634,7 @@ void TransactionExecutor::dmcCall(bcos::protocol::ExecutionMessage::UniquePtr in } // Create a temp storage - auto storage = createStateStorage(std::move(prev), true, - m_blockContext->features().get(ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); + auto storage = createStateStorage(std::move(prev), true, false); // Create a temp block context blockContext = createBlockContextForCall( @@ -1990,9 +1989,7 @@ void TransactionExecutor::getCode( std::unique_lock lock(m_stateStoragesMutex); if (!m_stateStorages.empty()) { - stateStorage = createStateStorage(m_stateStorages.back().storage, true, - m_blockContext->features().get( - ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); + stateStorage = createStateStorage(m_stateStorages.back().storage, true, false); } } // create temp state storage @@ -2000,15 +1997,11 @@ void TransactionExecutor::getCode( { if (m_cachedStorage) { - stateStorage = createStateStorage(m_cachedStorage, true, - m_blockContext->features().get( - ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); + stateStorage = createStateStorage(m_cachedStorage, true, false); } else { - stateStorage = createStateStorage(m_backendStorage, true, - m_blockContext->features().get( - ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); + stateStorage = createStateStorage(m_backendStorage, true, false); } } @@ -2119,14 +2112,11 @@ void TransactionExecutor::getABI( } storage::StateStorageInterface::Ptr stateStorage; - { std::unique_lock lock(m_stateStoragesMutex); if (!m_stateStorages.empty()) { - stateStorage = createStateStorage(m_stateStorages.back().storage, true, - m_blockContext->features().get( - ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); + stateStorage = createStateStorage(m_stateStorages.back().storage, true, false); } } // create temp state storage @@ -2134,15 +2124,11 @@ void TransactionExecutor::getABI( { if (m_cachedStorage) { - stateStorage = createStateStorage(m_cachedStorage, true, - m_blockContext->features().get( - ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); + stateStorage = createStateStorage(m_cachedStorage, true, false); } else { - stateStorage = createStateStorage(m_backendStorage, true, - m_blockContext->features().get( - ledger::Features::Flag::bugfix_set_row_with_dirty_flag)); + stateStorage = createStateStorage(m_backendStorage, true, false); } } diff --git a/bcos-ledger/src/libledger/Ledger.h b/bcos-ledger/src/libledger/Ledger.h index aacb7597a9..5edfd7b22a 100644 --- a/bcos-ledger/src/libledger/Ledger.h +++ b/bcos-ledger/src/libledger/Ledger.h @@ -138,7 +138,7 @@ class Ledger : public LedgerInterface auto [compatibilityVersionStr, _] = entry->template getObject(); auto const version = bcos::tool::toVersionNumber(compatibilityVersionStr); auto stateStorage = stateStorageFactory.createStateStorage( - m_storage, version, true, false, std::move(keyPageIgnoreTables)); + m_storage, version, true, false, keyPageIgnoreTables); return stateStorage; } return std::make_shared(m_storage, true); From ffe793d818e4c1e6f5a11469d018219962fc02ba Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Thu, 6 Jun 2024 16:39:54 +0800 Subject: [PATCH 32/39] (rpc): fix web3 rpc syncing response, transaction response. (#4495) --- .../bcos-framework/sync/BlockSyncInterface.h | 6 ++++ bcos-rpc/bcos-rpc/jsonrpc/Common.h | 2 +- bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp | 18 +++++++++++ .../web3jsonrpc/endpoints/EthEndpoint.cpp | 15 +++++++++- .../web3jsonrpc/model/TransactionResponse.h | 30 +++++++++++++++++++ bcos-sync/bcos-sync/BlockSync.cpp | 10 +++++++ bcos-sync/bcos-sync/BlockSync.h | 3 ++ 7 files changed, 82 insertions(+), 2 deletions(-) diff --git a/bcos-framework/bcos-framework/sync/BlockSyncInterface.h b/bcos-framework/bcos-framework/sync/BlockSyncInterface.h index f635a6835f..07ff2d8180 100644 --- a/bcos-framework/bcos-framework/sync/BlockSyncInterface.h +++ b/bcos-framework/bcos-framework/sync/BlockSyncInterface.h @@ -58,5 +58,11 @@ class BlockSyncInterface virtual bool faultyNode(bcos::crypto::NodeIDPtr _nodeID) = 0; virtual bool isSyncing() const { return false; } + + virtual std::optional> + getSyncStatus() const + { + return std::nullopt; + } }; } // namespace bcos::sync diff --git a/bcos-rpc/bcos-rpc/jsonrpc/Common.h b/bcos-rpc/bcos-rpc/jsonrpc/Common.h index 0475be053d..a701062cad 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/Common.h +++ b/bcos-rpc/bcos-rpc/jsonrpc/Common.h @@ -144,7 +144,7 @@ inline void nodeInfoToJson(Json::Value& _response, bcos::group::ChainNodeInfo::P { supportConfig.append(config); } - _response["supportConfig"] = std::move(supportConfig); + _response["supportConfigs"] = std::move(supportConfig); _response["protocol"] = protocolResponse; } diff --git a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp index 0c29b71257..4808e0c1cb 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp +++ b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -244,6 +245,23 @@ void bcos::rpc::toJsonResp(Json::Value& jResp, bcos::protocol::Transaction const jResp["extension"].append(ext); } } + if (transaction.type() == bcos::protocol::TransactionType::Web3Transacion) [[unlikely]] + { + Web3Transaction web3Tx; + auto extraBytesRef = + bcos::bytesRef(const_cast(transaction.extraTransactionBytes().data()), + transaction.extraTransactionBytes().size()); + codec::rlp::decode(extraBytesRef, web3Tx); + jResp["value"] = web3Tx.value.str(); + jResp["gasPrice"] = web3Tx.maxPriorityFeePerGas.str(); + jResp["gasLimit"] = web3Tx.gasLimit * 100; + if (web3Tx.type >= TransactionType::EIP1559) + { + jResp["maxPriorityFeePerGas"] = web3Tx.maxPriorityFeePerGas.str(); + jResp["maxFeePerGas"] = web3Tx.maxFeePerGas.str(); + } + codec::rlp::decode(extraBytesRef, web3Tx); + } } void bcos::rpc::toJsonResp(Json::Value& jResp, std::string_view _txHash, diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp index a07f131bcc..decf6d9a53 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/endpoints/EthEndpoint.cpp @@ -50,7 +50,20 @@ task::Task EthEndpoint::protocolVersion(const Json::Value&, Json::Value&) task::Task EthEndpoint::syncing(const Json::Value&, Json::Value& response) { auto const sync = m_nodeService->sync(); - Json::Value result = sync->isSyncing(); + auto status = sync->getSyncStatus(); + Json::Value result; + if (!status.has_value()) + { + result = false; + } + else + { + result = Json::objectValue; + auto [currentBlock, highestBlock] = status.value(); + result["startingBlock"] = "0x0"; + result["currentBlock"] = toQuantity(currentBlock); + result["highestBlock"] = toQuantity(highestBlock); + } buildJsonContent(result, response); co_return; } diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h index 2f4f0ad3f6..ae1d105770 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h @@ -116,12 +116,42 @@ static void combineTxResponse(Json::Value& result, bcos::protocol::Transaction:: result["nonce"] = toQuantity(web3Tx.nonce); result["type"] = toQuantity(static_cast(web3Tx.type)); result["value"] = toQuantity(web3Tx.value); + if (web3Tx.type >= TransactionType::EIP2930) + { + result["accessList"] = Json::arrayValue; + result["accessList"].resize(web3Tx.accessList.size()); + for (size_t i = 0; i < web3Tx.accessList.size(); i++) + { + auto& accessList = web3Tx.accessList[i]; + Json::Value access = Json::objectValue; + access["address"] = accessList.account.hexPrefixed(); + access["storageKeys"] = Json::arrayValue; + access["storageKeys"].resize(accessList.storageKeys.size()); + for (size_t j = 0; j < accessList.storageKeys.size(); j++) + { + Json::Value storageKey = accessList.storageKeys[j].hexPrefixed(); + access["storageKeys"].append(std::move(storageKey)); + } + result["accessList"].append(std::move(access)); + } + } if (web3Tx.type >= TransactionType::EIP1559) { result["maxPriorityFeePerGas"] = toQuantity(web3Tx.maxPriorityFeePerGas); result["maxFeePerGas"] = toQuantity(web3Tx.maxFeePerGas); } result["chainId"] = toQuantity(web3Tx.chainId.value_or(0)); + if (web3Tx.type >= TransactionType::EIP4844) + { + result["maxFeePerBlobGas"] = web3Tx.maxFeePerBlobGas.str(); + result["blobVersionedHashes"] = Json::arrayValue; + result["blobVersionedHashes"].resize(web3Tx.blobVersionedHashes.size()); + for (size_t i = 0; i < web3Tx.blobVersionedHashes.size(); i++) + { + Json::Value hash = web3Tx.blobVersionedHashes[i].hexPrefixed(); + result["blobVersionedHashes"].append(std::move(hash)); + } + } } result["r"] = toQuantity(tx->signatureData().getCroppedData(0, 32)); result["s"] = toQuantity(tx->signatureData().getCroppedData(32, 32)); diff --git a/bcos-sync/bcos-sync/BlockSync.cpp b/bcos-sync/bcos-sync/BlockSync.cpp index d115e589d1..dc5df88d61 100644 --- a/bcos-sync/bcos-sync/BlockSync.cpp +++ b/bcos-sync/bcos-sync/BlockSync.cpp @@ -292,6 +292,16 @@ bool BlockSync::isSyncing() const return (m_state == SyncState::Downloading); } +std::optional> +BlockSync::getSyncStatus() const +{ + if (!isSyncing()) + { + return std::nullopt; + } + return std::make_tuple(m_config->blockNumber(), m_config->knownHighestNumber()); +} + void BlockSync::maintainDownloadingBuffer() { if (m_downloadingQueue->size() == 0) diff --git a/bcos-sync/bcos-sync/BlockSync.h b/bcos-sync/bcos-sync/BlockSync.h index 545de9004a..16aae731a9 100644 --- a/bcos-sync/bcos-sync/BlockSync.h +++ b/bcos-sync/bcos-sync/BlockSync.h @@ -91,6 +91,9 @@ class BlockSync : public BlockSyncInterface, bool isSyncing() const override; + virtual std::optional> + getSyncStatus() const override; + protected: virtual void asyncNotifyBlockSyncMessage(Error::Ptr _error, bcos::crypto::NodeIDPtr _nodeID, bytesConstRef _data, std::function _sendResponse, From f973690dfe77a4cf860ee095b205d8c4fd819cad Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Wed, 19 Jun 2024 09:26:57 +0800 Subject: [PATCH 33/39] (rpc,hash): fix new transaction sync bug, fix EIP1559 signature bug. (#4497) --- .../signature/secp256k1/Secp256k1Crypto.h | 9 +- .../src/precompiled/common/Utilities.h | 2 + .../web3jsonrpc/model/Web3Transaction.cpp | 82 +++++++++++-------- bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp | 1 + .../bcos-tars-protocol/impl/TarsHashable.h | 10 +++ 5 files changed, 64 insertions(+), 40 deletions(-) diff --git a/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1Crypto.h b/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1Crypto.h index c413ee1eb5..e78408daae 100644 --- a/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1Crypto.h +++ b/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1Crypto.h @@ -25,10 +25,11 @@ namespace bcos::crypto { constexpr uint16_t SECP256K1_SIGNATURE_R_LEN = 32; -const int SECP256K1_SIGNATURE_LEN = 65; -const int SECP256K1_UNCOMPRESS_PUBLICKEY_LEN = 65; -const int SECP256K1_PUBLICKEY_LEN = 64; -const int SECP256K1_SIGNATURE_V = 64; +constexpr uint16_t SECP256K1_SIGNATURE_S_LEN = 32; +constexpr int SECP256K1_SIGNATURE_LEN = 65; +constexpr int SECP256K1_UNCOMPRESS_PUBLICKEY_LEN = 65; +constexpr int SECP256K1_PUBLICKEY_LEN = 64; +constexpr int SECP256K1_SIGNATURE_V = 64; std::shared_ptr secp256k1Sign(const KeyPairInterface& _keyPair, const HashType& _hash); bool secp256k1Verify(const PublicPtr& _pubKey, const HashType& _hash, bytesConstRef _signatureData); std::unique_ptr secp256k1GenerateKeyPair(); diff --git a/bcos-executor/src/precompiled/common/Utilities.h b/bcos-executor/src/precompiled/common/Utilities.h index 587b2e6a7b..85d1a1f663 100644 --- a/bcos-executor/src/precompiled/common/Utilities.h +++ b/bcos-executor/src/precompiled/common/Utilities.h @@ -89,6 +89,8 @@ inline std::string getDynamicPrecompiledCodeString( inline bool isDynamicPrecompiledAccountCode(const std::string_view& _code) { + // FIXME)): fix here, use bugfix + // return _code.starts_with(getDynamicPrecompiledCodeString(ACCOUNT_ADDRESS, "")); return std::string_view(getDynamicPrecompiledCodeString(ACCOUNT_ADDRESS, "")) == _code; } diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp index 38592d79cf..a280348739 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp @@ -294,6 +294,7 @@ bcos::Error::UniquePtr decode(bcos::bytesRef& in, Web3Transaction& out) noexcept { return BCOS_ERROR_UNIQUE_PTR(InputTooShort, "Input too short"); } + Error::UniquePtr decodeError = nullptr; if (auto const& firstByte = in[0]; 0 < firstByte && firstByte < BYTES_HEAD_BASE) { // EIP-2718: Transaction Type @@ -350,51 +351,60 @@ bcos::Error::UniquePtr decode(bcos::bytesRef& in, Web3Transaction& out) noexcept } } - return decodeItems(in, out.signatureV, out.signatureR, out.signatureS); + decodeError = decodeItems(in, out.signatureV, out.signatureR, out.signatureS); } - // rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s]) - auto&& [error, header] = decodeHeader(in); - if (error != nullptr) - { - return std::move(error); - } - if (!header.isList) + else { - return BCOS_ERROR_UNIQUE_PTR(UnexpectedList, "Unexpected list"); + // rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s]) + auto&& [error, header] = decodeHeader(in); + if (error != nullptr) + { + return std::move(error); + } + if (!header.isList) + { + return BCOS_ERROR_UNIQUE_PTR(UnexpectedList, "Unexpected list"); + } + out.type = TransactionType::Legacy; + decodeError = decodeItems(in, out.nonce, out.maxPriorityFeePerGas, out.gasLimit, out.to, + out.value, out.data, out.signatureV, out.signatureR, out.signatureS); + out.maxFeePerGas = out.maxPriorityFeePerGas; + // TODO: EIP-155 chainId decode from encoded bytes for sign + auto v = out.signatureV; + if (v == 27 || v == 28) + { + // pre EIP-155 + out.chainId = std::nullopt; + out.signatureV = v - 27; + } + else if (v == 0 || v == 1) + { + out.chainId = std::nullopt; + return decodeError; + } + else if (v < 35) + { + return BCOS_ERROR_UNIQUE_PTR(InvalidVInSignature, "Invalid V in signature"); + } + else + { + // https://eips.ethereum.org/EIPS/eip-155 + // Find chain_id and y_parity ∈ {0, 1} such that + // v = chain_id * 2 + 35 + y_parity + out.signatureV = (v - 35) % 2; + out.chainId = ((v - 35) >> 1); + } } - out.type = TransactionType::Legacy; - auto decodeError = decodeItems(in, out.nonce, out.maxPriorityFeePerGas, out.gasLimit, out.to, - out.value, out.data, out.signatureV, out.signatureR, out.signatureS); - if (out.signatureR.size() < crypto::SECP256K1_SIGNATURE_LEN / 2) + // rehandle signature and chainId + if (out.signatureR.size() < crypto::SECP256K1_SIGNATURE_R_LEN) { out.signatureR.insert( out.signatureR.begin(), crypto::SECP256K1_SIGNATURE_R_LEN - out.signatureR.size(), 0); } - if (out.signatureS.size() < crypto::SECP256K1_SIGNATURE_LEN / 2) + if (out.signatureS.size() < crypto::SECP256K1_SIGNATURE_S_LEN) { out.signatureS.insert(out.signatureS.begin(), - crypto::SECP256K1_SIGNATURE_R_LEN - out.signatureS.size(), bcos::byte(0)); - } - // TODO: EIP-155 chainId decode from encoded bytes for sign - out.maxFeePerGas = out.maxPriorityFeePerGas; - auto v = out.signatureV; - if (v == 27 || v == 28) - { - // pre EIP-155 - out.chainId = std::nullopt; - out.signatureV = v - 27; - } - else if (v < 35) - { - return BCOS_ERROR_UNIQUE_PTR(InvalidVInSignature, "Invalid V in signature"); - } - else - { - // https://eips.ethereum.org/EIPS/eip-155 - // Find chain_id and y_parity ∈ {0, 1} such that - // v = chain_id * 2 + 35 + y_parity - out.signatureV = (v - 35) % 2; - out.chainId = ((v - 35) >> 1); + crypto::SECP256K1_SIGNATURE_S_LEN - out.signatureS.size(), bcos::byte(0)); } return decodeError; } diff --git a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp index 4bf2034133..4f6e2ee2d3 100644 --- a/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp +++ b/bcos-rpc/test/unittests/rpc/Web3RpcTest.cpp @@ -343,6 +343,7 @@ BOOST_AUTO_TEST_CASE(handleEIP1559TxTest) Web3TestFixture* self, decltype(hashes) m_hashes) -> task::Task { auto txs = co_await self->txPool->getTransactions(m_hashes); BOOST_CHECK(txs.size() == 1); + BOOST_CHECK(txs[0] != nullptr); BOOST_CHECK(txs[0]->hash() == m_hashes[0]); BOOST_CHECK(txs[0]->type() == 1); BOOST_CHECK(!txs[0]->extraTransactionBytes().empty()); diff --git a/bcos-tars-protocol/bcos-tars-protocol/impl/TarsHashable.h b/bcos-tars-protocol/bcos-tars-protocol/impl/TarsHashable.h index 0d8dc1b7c8..fb6cf8cbb0 100644 --- a/bcos-tars-protocol/bcos-tars-protocol/impl/TarsHashable.h +++ b/bcos-tars-protocol/bcos-tars-protocol/impl/TarsHashable.h @@ -6,6 +6,7 @@ #include "bcos-tars-protocol/tars/Block.h" #include "bcos-tars-protocol/tars/Transaction.h" #include "bcos-tars-protocol/tars/TransactionReceipt.h" +#include #include #include @@ -16,6 +17,15 @@ void tag_invoke(bcos::concepts::hash::tag_t /*u Transaction const& transaction, bcos::crypto::hasher::Hasher auto&& hasher, bcos::concepts::bytebuffer::ByteBuffer auto& out) { + if (transaction.type >= static_cast(bcos::protocol::TransactionType::Web3Transacion)) + [[unlikely]] + { + if (!transaction.extraTransactionHash.empty()) [[likely]] + { + bcos::concepts::bytebuffer::assignTo(transaction.extraTransactionHash, out); + } + return; + } if (!transaction.dataHash.empty()) { bcos::concepts::bytebuffer::assignTo(transaction.dataHash, out); From 12c5d76f58caa425b6e935176b9f27d9f74bb8f3 Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Tue, 25 Jun 2024 08:56:42 +0800 Subject: [PATCH 34/39] (executor,feature): add bugfix_eoa_match_failed to fix eoa code match logic error, memstore latest timestamp for call request. (#4505) --- .../src/executor/TransactionExecutor.cpp | 21 ++++--- .../src/executor/TransactionExecutor.h | 1 + .../src/precompiled/common/Utilities.h | 8 ++- bcos-executor/src/vm/HostContext.cpp | 10 +++- .../bcos-framework/ledger/Features.h | 57 ++++++++++++++----- .../unittests/interfaces/FeaturesTest.cpp | 1 + .../bcos-pbft/pbft/engine/PBFTEngine.cpp | 10 ++++ bcos-sync/bcos-sync/state/SyncPeerStatus.cpp | 12 ++-- .../txpool/storage/MemoryStorage.cpp | 8 +++ 9 files changed, 97 insertions(+), 31 deletions(-) diff --git a/bcos-executor/src/executor/TransactionExecutor.cpp b/bcos-executor/src/executor/TransactionExecutor.cpp index 2ed03692f6..c0ea6cad99 100644 --- a/bcos-executor/src/executor/TransactionExecutor.cpp +++ b/bcos-executor/src/executor/TransactionExecutor.cpp @@ -181,6 +181,9 @@ TransactionExecutor::TransactionExecutor(bcos::ledger::LedgerInterface::Ptr ledg initTestPrecompiledTable(m_backendStorage); } + auto const header = getBlockHeaderInStorage(m_lastCommittedBlockNumber); + m_lastCommittedBlockTimestamp = header != nullptr ? header->timestamp() : utcTime(); + assert(m_precompiled != nullptr && m_precompiled->size() > 0); start(); } @@ -543,6 +546,7 @@ void TransactionExecutor::nextBlockHeader(int64_t schedulerTermId, m_ledgerCache->setBlockNumber2Hash( blockHeader->number() - 1, (*parentInfoIt).blockHash); } + m_lastCommittedBlockTimestamp = blockHeader->timestamp(); EXECUTOR_NAME_LOG(DEBUG) << BLOCK_NUMBER(blockHeader->number()) << "NextBlockHeader success" << LOG_KV("number", blockHeader->number()) @@ -637,8 +641,8 @@ void TransactionExecutor::dmcCall(bcos::protocol::ExecutionMessage::UniquePtr in auto storage = createStateStorage(std::move(prev), true, false); // Create a temp block context - blockContext = createBlockContextForCall( - m_lastCommittedBlockNumber + 1, h256(), utcTime(), m_blockVersion, std::move(storage)); + blockContext = createBlockContextForCall(m_lastCommittedBlockNumber, h256(), + m_lastCommittedBlockTimestamp, m_blockVersion, std::move(storage)); auto inserted = m_calledContext->emplace( std::tuple{input->contextID(), input->seq()}, CallState{blockContext}); @@ -807,8 +811,8 @@ void TransactionExecutor::call(bcos::protocol::ExecutionMessage::UniquePtr input createStateStorage(std::move(prev), true, false /*call storage no need set flag*/); // Create a temp block context - blockContext = createBlockContextForCall( - m_lastCommittedBlockNumber + 1, h256(), utcTime(), m_blockVersion, std::move(storage)); + blockContext = createBlockContextForCall(m_lastCommittedBlockNumber, h256(), + m_lastCommittedBlockTimestamp, m_blockVersion, std::move(storage)); auto inserted = m_calledContext->emplace( std::tuple{input->contextID(), input->seq()}, CallState{blockContext}); @@ -2078,9 +2082,12 @@ void TransactionExecutor::getCode( } auto code = entry->getField(0); - if (m_blockContext->features().get( - ledger::Features::Flag::bugfix_eoa_as_contract) && - bcos::precompiled::isDynamicPrecompiledAccountCode(code)) + if ((m_blockContext->features().get( + ledger::Features::Flag::bugfix_eoa_as_contract) && + bcos::precompiled::isDynamicPrecompiledAccountCode(code)) || + (m_blockContext->features().get( + ledger::Features::Flag::bugfix_eoa_match_failed) && + bcos::precompiled::matchDynamicAccountCode(code))) { EXECUTOR_NAME_LOG(DEBUG) << "Get eoa code success, return empty code to evm"; callback(nullptr, bcos::bytes()); diff --git a/bcos-executor/src/executor/TransactionExecutor.h b/bcos-executor/src/executor/TransactionExecutor.h index 069e03d02f..9b0e044b14 100644 --- a/bcos-executor/src/executor/TransactionExecutor.h +++ b/bcos-executor/src/executor/TransactionExecutor.h @@ -283,6 +283,7 @@ class TransactionExecutor : public ParallelTransactionExecutorInterface, std::list m_stateStorages; bcos::protocol::BlockNumber m_lastCommittedBlockNumber = getBlockNumberInStorage(); + std::atomic_uint64_t m_lastCommittedBlockTimestamp = utcTime(); struct HashCombine { diff --git a/bcos-executor/src/precompiled/common/Utilities.h b/bcos-executor/src/precompiled/common/Utilities.h index 85d1a1f663..9b06a5f401 100644 --- a/bcos-executor/src/precompiled/common/Utilities.h +++ b/bcos-executor/src/precompiled/common/Utilities.h @@ -87,10 +87,14 @@ inline std::string getDynamicPrecompiledCodeString( return boost::join(std::vector({PRECOMPILED_CODE_FIELD, _address, _params}), ","); } +inline bool matchDynamicAccountCode(std::string_view code) +{ + return code.starts_with(getDynamicPrecompiledCodeString(ACCOUNT_ADDRESS, "")); +} + +// ERROR: this method match account precompiled in wrong logic, use matchDynamicAccountCode instead. inline bool isDynamicPrecompiledAccountCode(const std::string_view& _code) { - // FIXME)): fix here, use bugfix - // return _code.starts_with(getDynamicPrecompiledCodeString(ACCOUNT_ADDRESS, "")); return std::string_view(getDynamicPrecompiledCodeString(ACCOUNT_ADDRESS, "")) == _code; } diff --git a/bcos-executor/src/vm/HostContext.cpp b/bcos-executor/src/vm/HostContext.cpp index 8e9e8c8197..1f578dafd2 100644 --- a/bcos-executor/src/vm/HostContext.cpp +++ b/bcos-executor/src/vm/HostContext.cpp @@ -483,9 +483,13 @@ bcos::bytes HostContext::externalCodeRequest(const std::string_view& address) request->staticCall = staticCall(); auto response = m_executive->externalCall(std::move(request)); - if (m_executive->blockContext().features().get( - ledger::Features::Flag::bugfix_eoa_as_contract) && - precompiled::isDynamicPrecompiledAccountCode(fromBytes(response->data))) + if ((m_executive->blockContext().features().get( + ledger::Features::Flag::bugfix_eoa_as_contract) && + precompiled::isDynamicPrecompiledAccountCode(fromBytes(response->data))) || + (m_executive->blockContext().features().get( + ledger::Features::Flag::bugfix_eoa_match_failed) && + bcos::precompiled::matchDynamicAccountCode( + std::string_view((char*)response->data.data(), response->data.size())))) { return bytes(); } diff --git a/bcos-framework/bcos-framework/ledger/Features.h b/bcos-framework/bcos-framework/ledger/Features.h index 3664d98dcc..3b032cbfce 100644 --- a/bcos-framework/bcos-framework/ledger/Features.h +++ b/bcos-framework/bcos-framework/ledger/Features.h @@ -42,6 +42,7 @@ class Features bugfix_empty_abi_reset, // support empty abi reset of same code bugfix_eip55_addr, bugfix_eoa_as_contract, + bugfix_eoa_match_failed, bugfix_evm_exception_gas_used, bugfix_dmc_deploy_gas_used, bugfix_staticcall_noaddr_return, @@ -138,34 +139,60 @@ class Features std::vector flags; }; const static auto upgradeRoadmap = std::to_array({ - {protocol::BlockVersion::V3_2_3_VERSION, {Flag::bugfix_revert}}, + {protocol::BlockVersion::V3_2_3_VERSION, + { + Flag::bugfix_revert, + }}, {protocol::BlockVersion::V3_2_4_VERSION, - {Flag::bugfix_statestorage_hash, - Flag::bugfix_evm_create2_delegatecall_staticcall_codecopy}}, + { + Flag::bugfix_statestorage_hash, + Flag::bugfix_evm_create2_delegatecall_staticcall_codecopy, + }}, {protocol::BlockVersion::V3_2_7_VERSION, - {Flag::bugfix_event_log_order, Flag::bugfix_call_noaddr_return, - Flag::bugfix_precompiled_codehash, Flag::bugfix_dmc_revert}}, + { + Flag::bugfix_event_log_order, + Flag::bugfix_call_noaddr_return, + Flag::bugfix_precompiled_codehash, + Flag::bugfix_dmc_revert, + }}, {protocol::BlockVersion::V3_5_VERSION, - {Flag::bugfix_revert, Flag::bugfix_statestorage_hash}}, + { + Flag::bugfix_revert, + Flag::bugfix_statestorage_hash, + }}, {protocol::BlockVersion::V3_6_VERSION, - {Flag::bugfix_statestorage_hash, + { + Flag::bugfix_statestorage_hash, Flag::bugfix_evm_create2_delegatecall_staticcall_codecopy, - Flag::bugfix_event_log_order, Flag::bugfix_call_noaddr_return, - Flag::bugfix_precompiled_codehash, Flag::bugfix_dmc_revert}}, + Flag::bugfix_event_log_order, + Flag::bugfix_call_noaddr_return, + Flag::bugfix_precompiled_codehash, + Flag::bugfix_dmc_revert, + }}, {protocol::BlockVersion::V3_6_1_VERSION, - {Flag::bugfix_keypage_system_entry_hash, - Flag::bugfix_internal_create_redundant_storage}}, + { + Flag::bugfix_keypage_system_entry_hash, + Flag::bugfix_internal_create_redundant_storage, + }}, {protocol::BlockVersion::V3_7_0_VERSION, - {Flag::bugfix_empty_abi_reset, Flag::bugfix_eip55_addr, + { + Flag::bugfix_empty_abi_reset, + Flag::bugfix_eip55_addr, Flag::bugfix_sharding_call_in_child_executive, - Flag::bugfix_internal_create_permission_denied}}, + Flag::bugfix_internal_create_permission_denied, + }}, {protocol::BlockVersion::V3_8_0_VERSION, - {Flag::bugfix_eoa_as_contract, Flag::bugfix_dmc_deploy_gas_used, - Flag::bugfix_evm_exception_gas_used, Flag::bugfix_set_row_with_dirty_flag}}, + { + Flag::bugfix_eoa_as_contract, + Flag::bugfix_dmc_deploy_gas_used, + Flag::bugfix_evm_exception_gas_used, + Flag::bugfix_set_row_with_dirty_flag, + }}, {protocol::BlockVersion::V3_9_0_VERSION, { Flag::bugfix_staticcall_noaddr_return, Flag::bugfix_support_transfer_receive_fallback, + Flag::bugfix_eoa_match_failed, }}, }); for (const auto& upgradeFeatures : upgradeRoadmap) diff --git a/bcos-framework/test/unittests/interfaces/FeaturesTest.cpp b/bcos-framework/test/unittests/interfaces/FeaturesTest.cpp index b2a2362333..34a6fd2e61 100644 --- a/bcos-framework/test/unittests/interfaces/FeaturesTest.cpp +++ b/bcos-framework/test/unittests/interfaces/FeaturesTest.cpp @@ -142,6 +142,7 @@ BOOST_AUTO_TEST_CASE(feature) "bugfix_empty_abi_reset", "bugfix_eip55_addr", "bugfix_eoa_as_contract", + "bugfix_eoa_match_failed", "bugfix_evm_exception_gas_used", "bugfix_dmc_deploy_gas_used", "bugfix_staticcall_noaddr_return", diff --git a/bcos-pbft/bcos-pbft/pbft/engine/PBFTEngine.cpp b/bcos-pbft/bcos-pbft/pbft/engine/PBFTEngine.cpp index 6b24d4b102..6484cbfb0e 100644 --- a/bcos-pbft/bcos-pbft/pbft/engine/PBFTEngine.cpp +++ b/bcos-pbft/bcos-pbft/pbft/engine/PBFTEngine.cpp @@ -233,6 +233,11 @@ void PBFTEngine::onProposalApplyFailed(int64_t _errorCode, PBFTProposalInterface _proposal->index() >= m_config->syncingHighestNumber()) { m_config->timer()->restart(); + // restart checkPoint timer to advoid timeout + if (m_timer->running()) + { + m_timer->restart(); + } PBFT_LOG(INFO) << LOG_DESC( "proposal execute failed and re-push the proposal " "into the cache") @@ -274,6 +279,11 @@ void PBFTEngine::onProposalApplySuccess( { m_config->timer()->restart(); } + // restart checkPoint timer to advoid timeout + if (m_timer->running()) + { + m_timer->restart(); + } m_cacheProcessor->addCheckPointMsg(checkPointMsg); m_cacheProcessor->setCheckPointProposal(_executedProposal); auto currentExpectedCheckPoint = m_config->expectedCheckPoint(); diff --git a/bcos-sync/bcos-sync/state/SyncPeerStatus.cpp b/bcos-sync/bcos-sync/state/SyncPeerStatus.cpp index 258ff86de6..5ddab55e17 100644 --- a/bcos-sync/bcos-sync/state/SyncPeerStatus.cpp +++ b/bcos-sync/bcos-sync/state/SyncPeerStatus.cpp @@ -167,7 +167,7 @@ void SyncPeerStatus::deletePeer(PublicPtr _peer) void SyncPeerStatus::foreachPeerRandom(std::function const& _f) const { - std::vector peersStatusList; + std::vector peersStatusList{}; { if (m_peersStatus.empty()) { @@ -176,17 +176,21 @@ void SyncPeerStatus::foreachPeerRandom(std::function cons peersStatusList.reserve(m_peersStatus.size()); // Get nodeid list - for (const auto& peer : m_peersStatus) + for (const auto& [_, peer] : m_peersStatus) { - peersStatusList.emplace_back(peer.second); + peersStatusList.emplace_back(peer); } } + if (peersStatusList.empty()) [[unlikely]] + { + return; + } // Random nodeid list for (size_t i = peersStatusList.size() - 1; i > 0; --i) { size_t select = rand() % (i + 1); - swap(peersStatusList[i], peersStatusList[select]); + std::swap(peersStatusList[i], peersStatusList[select]); } // access _f() according to the random list for (auto const& peerStatus : peersStatusList) diff --git a/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.cpp b/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.cpp index 5d8481500d..6c2ec4de75 100644 --- a/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.cpp +++ b/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.cpp @@ -412,6 +412,8 @@ TransactionStatus MemoryStorage::verifyAndSubmitTransaction( m_inRateCollector.update(1, true); if (result == TransactionStatus::None) { + auto const txImportTime = transaction->importTime(); + auto const txHash = transaction->hash().hex(); if (txSubmitCallback) { transaction->setSubmitCallback(std::move(txSubmitCallback)); @@ -424,6 +426,11 @@ TransactionStatus MemoryStorage::verifyAndSubmitTransaction( { result = insertWithoutLock(std::move(transaction)); } + if (c_fileLogLevel == TRACE) + { + TXPOOL_LOG(TRACE) << LOG_DESC("submitTxTime") << LOG_KV("txHash", txHash) + << LOG_KV("insertTime", utcTime() - txImportTime); + } } return result; @@ -1201,6 +1208,7 @@ std::shared_ptr MemoryStorage::batchVerifyProposal(Block::Ptr _block) << LOG_DESC("batchVerifyProposal unexpected wrong tx") << LOG_KV("blkNum", header->number()) << LOG_KV("blkHash", header->hash().abridged()) + << LOG_KV("txHash", accessor->value()->hash().hexPrefixed()) << LOG_KV("txBatchId", accessor->value()->batchId()) << LOG_KV("txBatchHash", accessor->value()->batchHash().abridged()); // NOTE: In certain scenarios, a bug may occur here: The leader generates the From a6e21ea5b6649d3c92d69b6e4b8c20edcbbf03e5 Mon Sep 17 00:00:00 2001 From: jdkuang <1950618179@qq.com> Date: Tue, 25 Jun 2024 12:13:11 +0800 Subject: [PATCH 35/39] (filter): add null parameter validation for the newFilter interface (#4502) Co-authored-by: jdkuang Co-authored-by: Kyon <32325790+kyonRay@users.noreply.github.com> --- bcos-rpc/bcos-rpc/filter/FilterRequest.cpp | 106 +++++++++++---------- 1 file changed, 58 insertions(+), 48 deletions(-) diff --git a/bcos-rpc/bcos-rpc/filter/FilterRequest.cpp b/bcos-rpc/bcos-rpc/filter/FilterRequest.cpp index 04816cb760..8f707cb22c 100644 --- a/bcos-rpc/bcos-rpc/filter/FilterRequest.cpp +++ b/bcos-rpc/bcos-rpc/filter/FilterRequest.cpp @@ -27,73 +27,83 @@ using namespace bcos::rpc; void FilterRequest::fromJson(const Json::Value& jParams, protocol::BlockNumber latest) { - // default: LatestBlockNumber - m_fromBlock = latest; - if (jParams.isMember("fromBlock") && !jParams["fromBlock"].isNull()) + // check params + if (!jParams.isMember("fromBlock") || jParams["fromBlock"].isNull()) { - std::tie(m_fromBlock, m_fromIsLatest) = - getBlockNumberByTag(latest, jParams["fromBlock"].asString()); + FILTER_LOG(ERROR) << LOG_BADGE("fromJson") << LOG_DESC("fromBlock is null"); + BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParamsCode(), "fromBlock is null")); } - // default: LatestBlockNumber - m_toBlock = latest; - if (jParams.isMember("toBlock") && !jParams["toBlock"].isNull()) + if (!jParams.isMember("toBlock") || jParams["toBlock"].isNull()) { - std::tie(m_toBlock, m_toIsLatest) = - getBlockNumberByTag(latest, jParams["toBlock"].asString()); + FILTER_LOG(ERROR) << LOG_BADGE("fromJson") << LOG_DESC("toBlock is null"); + BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParamsCode(), "toBlock is null")); } - if (jParams.isMember("address") && !jParams["address"].isNull()) + if (!jParams.isMember("address") || jParams["address"].isNull()) { - auto& jAddresses = jParams["address"]; - if (jAddresses.isArray()) - { - for (Json::Value::ArrayIndex index = 0; index < jAddresses.size(); ++index) - { - addAddress(jAddresses[index].asString()); - } - } - else - { - addAddress(jAddresses.asString()); - } + FILTER_LOG(ERROR) << LOG_BADGE("fromJson") << LOG_DESC("address is null"); + BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParamsCode(), "address is null")); } - if (jParams.isMember("topics") && !jParams["topics"].isNull()) + if (!jParams.isMember("topics") || jParams["topics"].isNull()) { - auto& jTopics = jParams["topics"]; + FILTER_LOG(ERROR) << LOG_BADGE("fromJson") << LOG_DESC("topics is null"); + BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParamsCode(), "topics is null")); + } + + // prase fromBlock + std::tie(m_fromBlock, m_fromIsLatest) = + getBlockNumberByTag(latest, jParams["fromBlock"].asString()); + + // prase toBlock + std::tie(m_toBlock, m_toIsLatest) = getBlockNumberByTag(latest, jParams["toBlock"].asString()); - // check number of topic - if (jTopics.size() > EVENT_LOG_TOPICS_MAX_INDEX) + // prase address + auto& jAddresses = jParams["address"]; + if (jAddresses.isArray()) + { + for (Json::Value::ArrayIndex index = 0; index < jAddresses.size(); ++index) { - FILTER_LOG(ERROR) << LOG_BADGE("fromJson") << LOG_DESC("exceed max topics") - << LOG_KV("topicsSize", jTopics.size()); - BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParamsCode(), "exceed max topics")); + addAddress(jAddresses[index].asString()); } - resizeTopic(jTopics.size()); - for (Json::Value::ArrayIndex index = 0; index < jTopics.size(); ++index) + } + else + { + addAddress(jAddresses.asString()); + } + + // prase topics + auto& jTopics = jParams["topics"]; + if (jTopics.size() > EVENT_LOG_TOPICS_MAX_INDEX) + { + FILTER_LOG(ERROR) << LOG_BADGE("fromJson") << LOG_DESC("exceed max topics") + << LOG_KV("topicsSize", jTopics.size()); + BOOST_THROW_EXCEPTION(JsonRpcException(InvalidParamsCode(), "exceed max topics")); + } + resizeTopic(jTopics.size()); + for (Json::Value::ArrayIndex index = 0; index < jTopics.size(); ++index) + { + auto& jIndex = jTopics[index]; + if (jIndex.isNull()) { - auto& jIndex = jTopics[index]; - if (jIndex.isNull()) - { - continue; - } + continue; + } - if (jIndex.isArray()) - { // array topics - for (Json::Value::ArrayIndex innerIndex = 0; innerIndex < jIndex.size(); - ++innerIndex) - { - addTopic(index, jIndex[innerIndex].asString()); - } - } - else - { // single topic, string value - addTopic(index, jIndex.asString()); + if (jIndex.isArray()) + { // array topics + for (Json::Value::ArrayIndex innerIndex = 0; innerIndex < jIndex.size(); ++innerIndex) + { + addTopic(index, jIndex[innerIndex].asString()); } } + else + { // single topic, string value + addTopic(index, jIndex.asString()); + } } + // prase blockhash if (jParams.isMember("blockhash") && !jParams["blockhash"].isNull()) { m_blockHash = jParams["blockhash"].asString(); From a7cb9db82a2b5a96aa0f88c8ab567ac8101ed022 Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Tue, 25 Jun 2024 19:43:14 +0800 Subject: [PATCH 36/39] (rpc): fix web3 transaction rpc response fields' bug. (#4508) --- .../bcos-framework/ledger/LedgerTypeDef.h | 2 +- .../bcos-framework/ledger/SystemConfigs.h | 1 + bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp | 11 ++- .../web3jsonrpc/model/TransactionResponse.h | 2 +- .../web3jsonrpc/model/Web3Transaction.cpp | 89 ++++++++++++++++++- .../web3jsonrpc/model/Web3Transaction.h | 6 +- bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp | 6 +- 7 files changed, 103 insertions(+), 14 deletions(-) diff --git a/bcos-framework/bcos-framework/ledger/LedgerTypeDef.h b/bcos-framework/bcos-framework/ledger/LedgerTypeDef.h index 0472ecacef..b134ead686 100644 --- a/bcos-framework/bcos-framework/ledger/LedgerTypeDef.h +++ b/bcos-framework/bcos-framework/ledger/LedgerTypeDef.h @@ -50,7 +50,7 @@ constexpr static std::string_view SYSTEM_KEY_RPBFT_EPOCH_SEALER_NUM = magic_enum constexpr static std::string_view SYSTEM_KEY_RPBFT_EPOCH_BLOCK_NUM = magic_enum::enum_name(SystemConfig::feature_rpbft_epoch_block_num); constexpr static std::string_view SYSTEM_KEY_RPBFT_SWITCH = magic_enum::enum_name(SystemConfig::feature_rpbft); // system configuration for balance -constexpr static std::string_view SYSTEM_KEY_BALANCE_PRECOMPILED_SWITCH = "feature_balance_precompiled"; +constexpr static std::string_view SYSTEM_KEY_BALANCE_PRECOMPILED_SWITCH = magic_enum::enum_name(SystemConfig::feature_balance_precompiled); // notify rotate key for rpbft constexpr static std::string_view INTERNAL_SYSTEM_KEY_NOTIFY_ROTATE = "feature_rpbft_notify_rotate"; // clang-format on diff --git a/bcos-framework/bcos-framework/ledger/SystemConfigs.h b/bcos-framework/bcos-framework/ledger/SystemConfigs.h index 89887fa8b0..91e99bfcf9 100644 --- a/bcos-framework/bcos-framework/ledger/SystemConfigs.h +++ b/bcos-framework/bcos-framework/ledger/SystemConfigs.h @@ -51,6 +51,7 @@ enum class SystemConfig feature_rpbft, feature_rpbft_epoch_block_num, feature_rpbft_epoch_sealer_num, + feature_balance_precompiled, web3_chain_id, }; class SystemConfigs diff --git a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp index 4808e0c1cb..bf0532d477 100644 --- a/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp +++ b/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp @@ -251,16 +251,19 @@ void bcos::rpc::toJsonResp(Json::Value& jResp, bcos::protocol::Transaction const auto extraBytesRef = bcos::bytesRef(const_cast(transaction.extraTransactionBytes().data()), transaction.extraTransactionBytes().size()); - codec::rlp::decode(extraBytesRef, web3Tx); + codec::rlp::decodeFromPayload(extraBytesRef, web3Tx); jResp["value"] = web3Tx.value.str(); - jResp["gasPrice"] = web3Tx.maxPriorityFeePerGas.str(); - jResp["gasLimit"] = web3Tx.gasLimit * 100; + jResp["gasLimit"] = web3Tx.gasLimit; if (web3Tx.type >= TransactionType::EIP1559) { jResp["maxPriorityFeePerGas"] = web3Tx.maxPriorityFeePerGas.str(); jResp["maxFeePerGas"] = web3Tx.maxFeePerGas.str(); + jResp["gasPrice"] = "0"; + } + else + { + jResp["gasPrice"] = web3Tx.maxPriorityFeePerGas.str(); } - codec::rlp::decode(extraBytesRef, web3Tx); } } diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h index ae1d105770..aad28b6e11 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/TransactionResponse.h @@ -112,7 +112,7 @@ static void combineTxResponse(Json::Value& result, bcos::protocol::Transaction:: Web3Transaction web3Tx; auto extraBytesRef = bcos::bytesRef(const_cast(tx->extraTransactionBytes().data()), tx->extraTransactionBytes().size()); - codec::rlp::decode(extraBytesRef, web3Tx); + codec::rlp::decodeFromPayload(extraBytesRef, web3Tx); result["nonce"] = toQuantity(web3Tx.nonce); result["type"] = toQuantity(static_cast(web3Tx.type)); result["value"] = toQuantity(web3Tx.value); diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp index a280348739..ad4f0e86fc 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.cpp @@ -33,7 +33,7 @@ using codec::rlp::decode; using codec::rlp::encode; using codec::rlp::header; using codec::rlp::length; -bcos::bytes Web3Transaction::encode() const +bcos::bytes Web3Transaction::encodeForSign() const { bcos::bytes out; if (type == TransactionType::Legacy) @@ -104,7 +104,7 @@ bcos::crypto::HashType Web3Transaction::txHash() const bcos::crypto::HashType Web3Transaction::hashForSign() const { - auto encodeForSign = this->encode(); + auto encodeForSign = this->encodeForSign(); return bcos::crypto::keccak256Hash(bcos::ref(encodeForSign)); } @@ -129,7 +129,7 @@ bcostars::Transaction Web3Transaction::takeToTarsTransaction() } tarsTx.type = static_cast(bcos::protocol::TransactionType::Web3Transacion); auto hashForSign = this->hashForSign(); - auto encodedForSign = this->encode(); + auto encodedForSign = this->encodeForSign(); // FISCO BCOS signature is r||s||v tarsTx.signature.reserve(crypto::SECP256K1_SIGNATURE_LEN); RANGES::move(this->signatureR, std::back_inserter(tarsTx.signature)); @@ -408,5 +408,88 @@ bcos::Error::UniquePtr decode(bcos::bytesRef& in, Web3Transaction& out) noexcept } return decodeError; } + +bcos::Error::UniquePtr decodeFromPayload(bcos::bytesRef& in, rpc::Web3Transaction& out) noexcept +{ + if (in.empty()) + { + return BCOS_ERROR_UNIQUE_PTR(InputTooShort, "Input too short"); + } + Error::UniquePtr decodeError = nullptr; + if (auto const& firstByte = in[0]; 0 < firstByte && firstByte < BYTES_HEAD_BASE) + { + // EIP-2718: Transaction Type + // EIP2930: 0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList, + // signatureYParity, signatureR, signatureS]) + + // EIP1559: 0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, + // gas_limit, destination, amount, data, access_list, signature_y_parity, signature_r, + // signature_s]) + + // EIP4844: 0x03 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, + // gas_limit, to, value, data, access_list, max_fee_per_blob_gas, blob_versioned_hashes, + // signature_y_parity, signature_r, signature_s]) + + out.type = static_cast(firstByte); + in = in.getCroppedData(1); + auto&& [e, header] = decodeHeader(in); + if (e != nullptr) + { + return std::move(e); + } + if (!header.isList) + { + return BCOS_ERROR_UNIQUE_PTR(UnexpectedString, "Unexpected String"); + } + uint64_t chainId = 0; + if (auto error = decodeItems(in, chainId, out.nonce, out.maxPriorityFeePerGas); + error != nullptr) + { + return error; + } + out.chainId.emplace(chainId); + if (out.type == TransactionType::EIP2930) + { + out.maxFeePerGas = out.maxPriorityFeePerGas; + } + else if (auto error = decode(in, out.maxFeePerGas); error != nullptr) + { + return error; + } + + if (auto error = decodeItems(in, out.gasLimit, out.to, out.value, out.data, out.accessList); + error != nullptr) + { + return error; + } + + if (out.type == TransactionType::EIP4844) + { + if (auto error = decodeItems(in, out.maxFeePerBlobGas, out.blobVersionedHashes); + error != nullptr) + { + return error; + } + } + return nullptr; + } + // rlp([nonce, gasPrice, gasLimit, to, value, data, chainId]) + auto&& [error, header] = decodeHeader(in); + if (error != nullptr) + { + return std::move(error); + } + if (!header.isList) + { + return BCOS_ERROR_UNIQUE_PTR(UnexpectedList, "Unexpected list"); + } + out.type = TransactionType::Legacy; + uint64_t chainId = 0; + decodeError = decodeItems(in, out.nonce, out.maxPriorityFeePerGas, out.gasLimit, out.to, + out.value, out.data, chainId); + out.chainId.emplace(chainId); + return decodeError; +} + } // namespace codec::rlp } // namespace bcos diff --git a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h index 9cc6a8ad3a..ac10693bcc 100644 --- a/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h +++ b/bcos-rpc/bcos-rpc/web3jsonrpc/model/Web3Transaction.h @@ -77,7 +77,8 @@ class Web3Transaction Web3Transaction& operator=(const Web3Transaction&) = delete; Web3Transaction& operator=(Web3Transaction&&) = default; - bcos::bytes encode() const; + // encode for sign, rlp(tx_payload) + bcos::bytes encodeForSign() const; // tx hash = keccak256(rlp(tx_payload,v,r,s)) bcos::crypto::HashType txHash() const; // hash for sign = keccak256(rlp(tx_payload)) @@ -101,7 +102,7 @@ class Web3Transaction sign.insert(sign.end(), signatureS.begin(), signatureS.end()); sign.push_back(signatureV); bcos::crypto::Keccak256 hashImpl; - auto encodeForSign = encode(); + auto encodeForSign = this->encodeForSign(); auto hash = bcos::crypto::keccak256Hash(ref(encodeForSign)); const bcos::crypto::Secp256k1Crypto signatureImpl; auto [_, addr] = signatureImpl.recoverAddress(hashImpl, hash, ref(sign)); @@ -159,5 +160,6 @@ Header header(const rpc::Web3Transaction& tx) noexcept; void encode(bcos::bytes& out, const rpc::Web3Transaction&) noexcept; bcos::Error::UniquePtr decode(bcos::bytesRef& in, rpc::AccessListEntry&) noexcept; bcos::Error::UniquePtr decode(bcos::bytesRef& in, rpc::Web3Transaction&) noexcept; +bcos::Error::UniquePtr decodeFromPayload(bcos::bytesRef& in, rpc::Web3Transaction&) noexcept; } // namespace codec::rlp } // namespace bcos diff --git a/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp b/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp index b0668c85dd..d6e6225d05 100644 --- a/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp +++ b/bcos-rpc/test/unittests/rpc/Web3TypeTest.cpp @@ -198,7 +198,7 @@ BOOST_AUTO_TEST_CASE(recoverAddress) auto rawTx2 = toHexStringWithPrefix(encoded); BOOST_CHECK_EQUAL(rawTx, rawTx2); - auto encodeForSign = tx.encode(); + auto encodeForSign = tx.encodeForSign(); bcos::bytes sign{}; sign.insert(sign.end(), tx.signatureR.begin(), tx.signatureR.end()); sign.insert(sign.end(), tx.signatureS.begin(), tx.signatureS.end()); @@ -243,7 +243,7 @@ BOOST_AUTO_TEST_CASE(EIP1559Recover) auto rawTx2 = toHexStringWithPrefix(encoded); BOOST_CHECK_EQUAL(rawTx, rawTx2); - auto encodeForSign = tx.encode(); + auto encodeForSign = tx.encodeForSign(); bcos::bytes sign{}; sign.insert(sign.end(), tx.signatureR.begin(), tx.signatureR.end()); sign.insert(sign.end(), tx.signatureS.begin(), tx.signatureS.end()); @@ -316,7 +316,7 @@ BOOST_AUTO_TEST_CASE(EIP4844Recover) auto rawTx2 = toHexStringWithPrefix(encoded); BOOST_CHECK_EQUAL(rawTx, rawTx2); - auto encodeForSign = tx.encode(); + auto encodeForSign = tx.encodeForSign(); bcos::bytes sign{}; sign.insert(sign.end(), tx.signatureR.begin(), tx.signatureR.end()); sign.insert(sign.end(), tx.signatureS.begin(), tx.signatureS.end()); From e489f4e8547d7ac8b569d57d931ab6849def0571 Mon Sep 17 00:00:00 2001 From: wenlinli <1574249665@qq.com> Date: Thu, 27 Jun 2024 17:39:57 +0800 Subject: [PATCH 37/39] add log_level debug setup for build_chain.sh (#4511) --- tools/BcosAirBuilder/build_chain.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/BcosAirBuilder/build_chain.sh b/tools/BcosAirBuilder/build_chain.sh index 7434f66e78..3aa1478a1f 100755 --- a/tools/BcosAirBuilder/build_chain.sh +++ b/tools/BcosAirBuilder/build_chain.sh @@ -67,6 +67,7 @@ modify_node_path="" multi_ca_path="" consensus_type="pbft" supported_consensus=(pbft rpbft) +log_level="info" # for pro or max default setting bcos_builder_package=BcosBuilder.tgz @@ -577,6 +578,7 @@ air -c [Required when expand node] Specify the path of the expanded node config.ini, config.genesis and p2p connection file nodes.json -d [Required when expand node] When expanding the node, specify the path where the CA certificate and private key are located -D Default off. If set -D, build with docker + -E Default off. If set -E, enable debug log -a [Optional] when Auth mode Specify the admin account address. -w [Optional] Whether to use the wasm virtual machine engine, default is false -R [Optional] Whether to use serial execute,default is true @@ -647,7 +649,7 @@ EOF } parse_params() { - while getopts "l:C:c:o:e:t:p:d:g:G:L:v:i:I:M:k:zwDshHmn:R:a:N:u:y:r:V:6T:" option; do + while getopts "l:C:c:o:e:t:p:d:g:G:L:v:i:I:M:k:zwDshHmEn:R:a:N:u:y:r:V:6T:" option; do case $option in 6) use_ipv6="true" && default_listen_ip="::" ;; @@ -699,6 +701,9 @@ parse_params() { m) monitor_mode="true" ;; + E) + log_level="debug" + ;; n) node_key_dir="${OPTARG}" dir_must_exists "${node_key_dir}" @@ -1534,7 +1539,7 @@ generate_common_ini() { enable_console_output = false log_path=./log ; info debug trace - level=info + level=${log_level} ; MB max_log_file_size=1024 ; rotate the log every hour From f4ccfbf1f8523eeb5a956e2890d96d1e0fddc76c Mon Sep 17 00:00:00 2001 From: Kyon <32325790+kyonRay@users.noreply.github.com> Date: Mon, 1 Jul 2024 09:59:07 +0800 Subject: [PATCH 38/39] (precompiled): fix web3_chain_id set config bug. (#4513) --- bcos-executor/src/precompiled/SystemConfigPrecompiled.cpp | 5 +++-- bcos-tool/bcos-tool/Exceptions.h | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bcos-executor/src/precompiled/SystemConfigPrecompiled.cpp b/bcos-executor/src/precompiled/SystemConfigPrecompiled.cpp index 93e7ac18f6..d888296f66 100644 --- a/bcos-executor/src/precompiled/SystemConfigPrecompiled.cpp +++ b/bcos-executor/src/precompiled/SystemConfigPrecompiled.cpp @@ -146,8 +146,8 @@ SystemConfigPrecompiled::SystemConfigPrecompiled(crypto::Hash::Ptr hashImpl) : P SYSTEM_KEY_WEB3_CHAIN_ID, [](const std::string& _value, uint32_t blockVersion) -> uint64_t { if (blockVersion < BlockVersion::V3_9_0_VERSION) { - BOOST_THROW_EXCEPTION( - PrecompiledError(fmt::format("unsupported key {}", SYSTEM_KEY_WEB3_CHAIN_ID))); + BOOST_THROW_EXCEPTION(bcos::tool::InvalidVersion( + fmt::format("unsupported key {}", SYSTEM_KEY_WEB3_CHAIN_ID))); } uint64_t number = 0; try @@ -304,6 +304,7 @@ int64_t SystemConfigPrecompiled::validate( } catch (bcos::tool::InvalidSetFeature const& e) { + /// PRECOMPILED_LOG(INFO) << LOG_DESC("SystemConfigPrecompiled: set feature failed") << LOG_KV("info", boost::diagnostic_information(e)); BOOST_THROW_EXCEPTION(PrecompiledError(*boost::get_error_info(e))); diff --git a/bcos-tool/bcos-tool/Exceptions.h b/bcos-tool/bcos-tool/Exceptions.h index 2b6738bcb0..527b8b6b52 100644 --- a/bcos-tool/bcos-tool/Exceptions.h +++ b/bcos-tool/bcos-tool/Exceptions.h @@ -27,6 +27,7 @@ namespace tool DERIVE_BCOS_EXCEPTION(LedgerConfigFetcherException); DERIVE_BCOS_EXCEPTION(InvalidConfig); DERIVE_BCOS_EXCEPTION(InvalidVersion); +/// NOTE: only use it when the compatibility version >= 3.6.0 DERIVE_BCOS_EXCEPTION(InvalidSetFeature); class ExceptionHolder From f4e3772e35ad60600f4c6c074302d7b9f4be71d1 Mon Sep 17 00:00:00 2001 From: kyonRay Date: Tue, 2 Jul 2024 16:51:21 +0800 Subject: [PATCH 39/39] (readme): update readme to 3.9.0. --- README.md | 2 +- transaction-scheduler/tests/testMultiLayerStorage.cpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bbfc88f7fd..41a4db1298 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ FISCO BCOS(读作/ˈfɪskl bi:ˈkɒz/) 是一个稳定、高效、安全的 ## 版本信息 - 稳定版本(生产环境使用):v3.2.7,版本内容可参考[《FISCO-BCOS v3.2.7版本说明》](https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v3.2.7) -- 最新版本(用户体验新特性):v3.8.0,版本内容可参考 [《FISCO-BCOS v3.8.0版本说明》](https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v3.8.0) +- 最新版本(用户体验新特性):v3.9.0,版本内容可参考 [《FISCO-BCOS v3.9.0版本说明》](https://github.com/FISCO-BCOS/FISCO-BCOS/releases/tag/v3.9.0) ## 系统概述 FISCO BCOS系统架构包括基础层、核心层、服务层、用户层和接入层提供稳定、安全的区块链底层服务。中间件层通过可视化界面,简化了用户管理区块链系统的流程。右侧配套相关开发、运维、安全控制的组件,辅助应用落地过程中不同角色的需要;同时,提供隐私保护和跨链相关的技术组件,满足不同场景的应用诉求。 diff --git a/transaction-scheduler/tests/testMultiLayerStorage.cpp b/transaction-scheduler/tests/testMultiLayerStorage.cpp index 30ed4a58bf..7905280156 100644 --- a/transaction-scheduler/tests/testMultiLayerStorage.cpp +++ b/transaction-scheduler/tests/testMultiLayerStorage.cpp @@ -75,8 +75,9 @@ BOOST_AUTO_TEST_CASE(merge) task::syncWait([this]() -> task::Task { auto view = std::make_optional(multiLayerStorage.fork()); view->newMutable(); - auto toKey = RANGES::views::transform( - [](int num) { return StateKey{"test_table"sv, fmt::format("key: {}", num)}; }); + auto toKey = RANGES::views::transform([](int num) { + return StateKey{"test_table"sv, fmt::format("key: {}", num)}; + }); auto toValue = RANGES::views::transform([](int num) { storage::Entry entry; entry.set(fmt::format("value: {}", num));